Wednesday, September 26, 2012

Minesweeper

I'm way more proud of this than I should be.  The code looks like ass, but it (mostly) works!  There are a few kinks here and there, but I'm pretty satisfied with the result so far! (Copy and paste into Python if you want to play)




#Minesweeper

import random

def drawBoard(board, dimensionx, dimensiony):
    print('      ', end='')
    for i in range(int(dimensionx[0]) + 1): #Print tens digits
        print(i, end='')
        print(' ' * 9, end='')
       
    print()
    print('      ', end='')
   
    for i in range(int(dimensionx[0])):  #Print ones digits
        print('0123456789', end='')
    for i in range(int(dimensionx[1]) +1): #Print remainder ones digits
        if i == int(dimensionx[1]):
            print(i) #Don't continue the line after the last digits
        else:
            print(i, end='')

    for i in range(dimensiony + 1):  #Print rows
        if i < 10:      #Add extra space if row number is single digit
            extraSpace = ' '
        else:
            extraSpace = ''
        print('  %s%s |%s| %s' %(i, extraSpace,rowData(board, dimensionx, i), i))

    print('      ', end='')

    for i in range(int(dimensionx[0])):  #Print ones digits
        print('0123456789', end='')
    for i in range(int(dimensionx[1]) +1): #Print remainder ones digits
        if i == int(dimensionx[1]):
            print(i) #Don't continue the line after the last digits
        else:
            print(i, end='')
           
    print('      ', end='')
    for i in range(int(dimensionx[0]) + 1): #Print tens digits
        print(i, end='')
        print(' ' * 9, end='')
    print()
    print()
   
def rowData(board, dimensionx, i): #Show what's currently on the board in the row
    rowDisplay = ''
    xValue = int(''.join(dimensionx))
    for x in range(xValue + 1):
        rowDisplay += board[x][i]
    return rowDisplay
       
def newBoard(board, dimensionx, dimensiony): #Gets theoretical board to start game
    xValue = int(''.join(dimensionx))
    yValue = dimensiony
    for x in range(xValue + 1):
        board.append([])
        for y in range(yValue + 1):
            board[x].append('*')

def getNumMines(xValue, yValue): #Sets the mines on the board
    area = float(xValue * yValue)
       
    numMines = int(area* .1) # 10% of the map is loaded with mines

    return numMines

def makeMines(xValue, yValue, numMines):  
    mines = []

    for i in range(numMines): #Place the mines randomly on the board
        mines.append([random.randint(0,xValue), random.randint(0,yValue)])

    return mines

def clearZeros(board, moveX, moveY, numMines, mines, xValue, yValue):
    board[moveX][moveY] = ' ' #Erase zero of the value currently being evaluated

    #All conditionals make sure that the max and min values of the board
    #have not been exceeded
   
    if moveX + 1 <= xValue and board[moveX+1][moveY] != ' ':
        checkBoard(board, moveX+1, moveY, numMines, mines, xValue, yValue) #Checks the space to the right of the player's move
    if moveX - 1 >= 0 and board[moveX-1][moveY] != ' ':
        checkBoard(board, moveX-1, moveY, numMines, mines, xValue, yValue) #Checks space to the left of the player's move
    if moveY + 1 <= yValue and board[moveX][moveY+1] != ' ':
        checkBoard(board, moveX, moveY+1, numMines, mines, xValue, yValue) #Checks space above player's move
    if moveY - 1 >= 0 and board[moveX][moveY-1] != ' ':
        checkBoard(board, moveX, moveY-1, numMines, mines, xValue, yValue) #Checks space below player's move

    if moveX + 1 <= xValue and board[moveX+1][moveY] == '0': #Repeats process for each new 0 found
        clearZeros(board, moveX+1, moveY, numMines, mines, xValue, yValue)
             
    if moveX - 1 >= 0 and board[moveX-1][moveY] == '0':
        clearZeros(board, moveX-1, moveY, numMines, mines, xValue, yValue)
             
    if moveY + 1 <= yValue and board[moveX][moveY+1] == '0':
        clearZeros(board, moveX, moveY+1, numMines, mines, xValue, yValue)
             
    if moveY - 1 >= 0 and board[moveX][moveY-1] == '0':
        clearZeros(board, moveX, moveY-1, numMines, mines, xValue, yValue)

def checkBoard(board, moveX, moveY, numMines, mines, xValue, yValue):
    dupeBoard= []
   
    for x in range(xValue + 1):    #Create a duplicate board to check
        dupeBoard.append([])
        for y in range(yValue + 1):
            dupeBoard[x].append(board[x][y])

    if makeMove(dupeBoard, [str(moveX), str(moveY)], mines, numMines, xValue, yValue) == '0':
        board[moveX][moveY] = '0'

    #If the analyzed space has adjacent mines next to it, display the number
    if makeMove(dupeBoard, [str(moveX), str(moveY)], mines, numMines, xValue, yValue) in '123456789':
        makeMove(board, [str(moveX), str(moveY)], mines, numMines, xValue, yValue)  
   
def makeMove(board, move, mines, numMines, xValue, yValue):
    moveX = int(move[0])
    moveY = int(move[1])
    adjacentMines = 0

    for i in range(len(move)):          #if a spot has been flagged, mark it and exit the function
        if move[i].lower() == 'flag' and board[moveX][moveY] == 'F':        #If a flag is already there, remove it
            board[moveX][moveY] = '*'
            return board[moveX][moveY]
        if move[i].lower() == 'flag':
            board[moveX][moveY] = 'F'
            return board[moveX][moveY]

    for i in range(numMines):
        if moveX == mines[i][0] and moveY == mines[i][1]:
            board[moveX][moveY] = 'X'
            return board[moveX][moveY]
   
    for i in range(numMines):
        if abs(moveX - mines[i][0]) <= 1 and abs(moveY - mines[i][1]) <= 1:  #If mine is adjacent, increment the number
            adjacentMines += 1

    board[moveX][moveY] = str(adjacentMines)

    return board[moveX][moveY]
       
def getDimensions():
    dimensions = []
    dimensionx = []
    dimensiony = 0 #y does not need to have its digits separated
    xint = 0
    yint = 0
   
    while (xint < 10 or xint > 99) or (yint < 10 or yint > 99):
        print('Please enter the size of your game board (x y) (min: 10 max: 99)')
        dimensions = input().split()
        if dimensions[0].isdigit() and dimensions[1].isdigit():
            xint = int(dimensions[0])
            yint = int(dimensions[1])
   
    for i in range(2):                      #split the x value entered into its 2
        dimensionx.append(dimensions[0][i]) #digits, and add them to the x list

    return [dimensionx, yint]
   
def validMove(move, xValue, yValue):
    moveX = int(move[0])
    moveY = int(move[1])
   
    return(move[0].isdigit() and move[1].isdigit and moveX <= xValue and moveX >= 0 and moveY <=yValue and moveY >= 0)

def playAgain():
    print('Would you like to play again?')
    return input().lower().startswith('y')

while True: #Game loop
    board = []
    dimensionx, dimensiony = getDimensions()
    xValue = int(''.join(dimensionx))  #Create integers of the dimensions to use in functions
    yValue = dimensiony
    newBoard(board, dimensionx, dimensiony) #Create a new board at the start of every new game
    drawBoard(board, dimensionx, dimensiony) #Print a board for the player to see
    numMines = getNumMines(xValue, yValue)
    mines = makeMines(xValue, yValue, numMines)

    while True: #Input loop
        print('Input x and y coordinates (x y).  If you want to flag a spot, type "Flag" after the coordinates.') #Get a valid move from the player
        move = list(input().split())
        while not validMove(move, xValue, yValue):
            print('Input x and y coordinates (x y).  If you want to flag a spot, type "Flag" after the coordinates.')
            move = list(input().split())

        moveX = int(move[0]) #Create integers of the move coordinates for some functions
        moveY = int(move[1])
       
        moveCheck = makeMove(board, move, mines, numMines, xValue, yValue)

        if moveCheck == '0':
            clearZeros(board, moveX, moveY, numMines, mines, xValue, yValue)
       
        drawBoard(board, dimensionx, dimensiony)

        if board[int(move[0])][int(move[1])] == 'X': #If a mine is hit, break out of the input loop and ask to play again.
            print('You hit a mine!  Game over!')
            break
       
        markedMines = 0
        for i in range(numMines): #After every move, check to see if all of the mines have been flagged
            mineX, mineY = mines[i]
            if board[mineX][mineY] == 'F':
                markedMines += 1
        if markedMines == numMines: #If all mines have been flagged, display winning message and break out of input loop
            print('Congratulations!  You\'ve cleared the minefield!')
            break
       
    if not playAgain():
        break

No comments:

Post a Comment