Desperately trying to create a code for a python TicTacToe game.
Im quite new to Python so am stuck with a feature that I want to add.
The idea is that the player can select whether he wants to play with 2, 3 players or against the computer.
He then selects whether he would like to be X or O (or Y for 3 players)
He then selects any boardsize up to 9x9.
So far so good, the two player, NbyN and X or O selection works and I have split the code into three seperate files (two classes) for ease.
I am lost with trying to figure out how to create the computer algorithm and to extend the game to 3 players.
Has anyone got any idea how I could do this?
Below is the code:
Player File:
class Player:
def __init__(self, player_name, shape):
self.player_name = player_name
self.shape = shape
def get_player_loc_input(self, rows, columns):
player_input = input('Enter in location for your move: ') # player input is with respect to field index location/values
converted_input = int(player_input)
if 1 <= converted_input <= (rows * columns): # bound checking
converted_input -= 1 # adjust from n+1 to n
transformed_value = (rows-(converted_input//columns)-1, converted_input%columns) # (row,col) tuple obj
return transformed_value
else:
raise ValueError('Input is not an index on the playing field. Try again\n')#
TicTac File:
class TicTac:
def __init__(self, rows, columns):
#self.playing_field = [ [7,8,9], [4,5,6], [1,2,3] ] #init the playing field to their respective nums on the numpad: 3x3
self.winner_found = False
self.is_full = False
self.possible_moves_left = columns * rows
self.rows = rows
def DefineBoard():
field = []
value = (rows * columns)
for row in range(rows):
field.append([])
for col in range(columns):
field[row].insert(0, value)
value = value - 1
return field
self.playing_field = DefineBoard()
#value = (rows * columns)
#def decrement(x): return (x-1)
#self.playing_field = [ [ decrement(value) for i in range(columns) ] for i in range(rows)]
def DrawBoard(self): #prints playing field based on size input
print('-------------')
for list in self.playing_field:
print('| ', end='')
for item in list:
print(str(item) + ' | ', end='')
print('\n-------------')
def Instructions(self):
print('\n-------------------------------------')
print('When entering in the location please enter the number of the index you want to replace with your shape.')
print('\nPrinting Initial Playing Field...')
self.DrawBoard()
print('\nLet the game begin!')
print('-------------------------------------')
def PlayerMove(self, index, shape):
row, col = index[0], index[1]
field_value = self.playing_field[row][col]
#if the value is of type int we can replace it
if isinstance(field_value, int):
self.playing_field[row][col] = shape #overwrite the value
self.possible_moves_left -= 1 #reduce the moves left
#check possible moves after its been updated
if self.possible_moves_left == 0:
self.is_full = True
raise EnvironmentError('All index posistions filled.\nGame Over. Nobody won.')
#else its the Player's shape (string)
else:
raise ValueError('Invalid Index. Position already filled. Try again.\n')
def ConsecutiveSymbols(self):
def check_list(passed_list):
#fast & quick check to tell if the "row" is incomplete
if isinstance(passed_list[0], str):
player_shape = passed_list[0] # set to first val
#compare the values to each other
for val in passed_list:
if isinstance(val, int) or player_shape != val:
return False #we found an inconsistency
return True #everything matched up
def Diagonal(orientation):
DiagonalList = []
counter = 0 if orientation is 'LtR' else self.rows-1
for row in self.playing_field:
DiagonalList.append(row[counter])
counter = counter+1 if orientation is 'LtR' else counter-1
return DiagonalList
# check rows for match
for row_list in self.playing_field:
if check_list(row_list):
return True
#check cols for match
transposed_playing_field = [list(a) for a in zip(*self.playing_field)] #convert our tuples from zip to a list format
for col_list in transposed_playing_field:
if check_list(col_list):
return True
#check diagonals for match
if check_list(Diagonal('LtR')): #LtR \ gets replaced each time we check
return True
if check_list(Diagonal('RtL')): # RtL / gets replaced each time we check
return True
return False #if we got here then no matches were found
Main File:
try:
from .TicTac import TicTac
from .Player import Player
except Exception:
from TicTac import TicTac
from Player import Player
winners=[]
def GameBegins():
Game=input("Would you like to play with 2, 3 players or against the CPU?").upper()
if Game=="2":
selection=input("Player 1: Would you like to play as X or O?").upper()
if selection == "X":
Players = {'Player_1': Player('Player_1', "X"), 'Player_2': Player('Player_2', "O")}
elif selection == "O":
Players = {'Player_1': Player('Player_1', "O"), 'Player_2': Player('Player_2', "X")}
else:
print("Please enter either X or O")
return False
if Game=="3":
selection=input("Player 1: Would you like to play as X, O or Y?").upper()
if selection == "X":
selection2=input("Player 2: Would you like to play as O or Y?").upper()
if selection2=="O":
Players = {'Player_1': Player('Player_1', "X"),"Player_2":Player("Player_2","O"),"Player_3":Player("Player_3","Y")}
elif selection2=="Y":
Players = {'Player_1': Player('Player_1', "X"),"Player_2":Player("Player_2","Y"),"Player_3":Player("Player_3","O")}
else:
print("Please enter either O or Y")
return False
elif selection == "O":
selection2=input("Player 2: Would you like to play as X or Y?").upper()
if selection2=="X":
Players = {'Player_1': Player('Player_1', "O"),"Player_2":Player("Player_2","X"),"Player_3":Player("Player_3","Y")}
elif selection2=="Y":
Players = {'Player_1': Player('Player_1', "O"),"Player_2":Player("Player_2","Y"),"Player_3":Player("Player_3","X")}
else:
print("Please enter either X or Y")
return False
elif selection=="Y":
selection2=input("Player 2: Would you like to play as X or O?").upper()
if selection2=="X":
Players = {'Player_1': Player('Player_1', "Y"),"Player_2":Player("Player_2","X"),"Player_3":Player("Player_3","O")}
elif selection2=="O":
Players = {'Player_1': Player('Player_1', "Y"),"Player_2":Player("Player_2","O"),"Player_3":Player("Player_3","X")}
else:
print("Please enter either X or O")
return False
if Game=="CPU":
CPU()
x=input("enter boardsize: ")
if x >="2":
rows, columns = int(x),int(x) #Players input becomes board size
else:
print("Please enter a boardsize of 3 or more")
GameBegins()
global game
game = TicTac(rows, columns)
game.Instructions()
player_id = 'Player_1' # index to swap between players, Player_1 starts
while (game.winner_found == False and game.is_full == False):
print('\nIt\'s ' + Players[player_id].player_name + ' Turn')
# loop until user inputs correct index value
while True:
try:
index = Players[player_id].get_player_loc_input(rows,columns)
shape = Players[player_id].shape
game.PlayerMove(index, shape)
except ValueError as msg:
print(msg)
continue
except EnvironmentError as msg:
print(msg)
break
game.winner_found = game.ConsecutiveSymbols() # check if a player has won
game.DrawBoard()
if game.winner_found:
print(Players[player_id].player_name + ' has won!') # print player who won
winners.append(str(Players[player_id].player_name))
player_id = 'Player_2' if player_id is 'Player_1' else 'Player_1' # switch between the 2 players
Replay() # Game has ended. Play Again?
def Replay():
PlayerDecision = input('\nDo you want to play again? (Y/N) ')
PlayerDecision = PlayerDecision.upper() #force to uppercase for consistency
if PlayerDecision == 'Y':
GameBegins()
elif PlayerDecision == 'N':
file=open("winners.txt","w")
for w in winners:
file.write(str(winners))
file.close()
exit(0)
else:
print('Incorrect input.')
Replay()
def main():
while True:
GameBegins()
if __name__ == '__main__':
main()
Would really appreciate some suggestions, thanks in advance!
Related
This question already has answers here:
How do I clone a list so that it doesn't change unexpectedly after assignment?
(24 answers)
Closed 1 year ago.
When I'm trying to do the move or more specifically change the element in the nested list, depending on what move to do, I'm instead changing the last element in all of the nested lists. I think I've constructed the list with the nested list correctly and that the problem lies in my method show_board. However I can't see what I'm doing wrong.
Here is my code:
class TicTacToe:
def __init__(self, sz):
self.sz = sz
self.board = []
def create_board(self):
"""Creating the game board with the given size."""
self.column = []
for _ in range(self.sz):
self.column.append('-')
self.board.append(self.column)
return self.board
def check_board(self):
"""Checking if the board is full."""
for row in self.board:
for spot in row:
if spot == '-':
return False
return True
def check_win(self, player):
"""Method for checking if a player has won the game. Evaluating rows, columns and
diagonals."""
win = None
#Checking rows for win:
for row in range(len(self.board)):
# win = True
for column in range(len(self.column)):
if self.board[row][column] == player:
continue
else:
win = False
return win
win = True
return win
#Checking columns for win:
for column in range(len(self.column)):
for row in range(len(self.board)):
if self.board[column][row] == player:
continue
else:
win = False
return win
win = True
return win
#Checking first diagonal for win:
for spot in range(len(self.board)):
if self.board[spot][spot] == player:
continue
elif self.board[spot][spot] == '-':
win = False
return win
win = True
return win
#Checking second diagonal for win:
for spot in range(len(self.board)):
if self.board[len(self.board)-spot-1][len(self.board)-spot-1] == player:
continue
elif self.board[len(self.board)-spot-1][len(self.board)-spot-1] == player:
win = False
return win
win = True
return win
def show_board(self):
"""Showing the current board"""
for row in self.board:
for item in row:
print(item, end=" ")
print()
def get_random_first_player(self):
"""Randomizing the first player."""
if random.randint(0,1) == 0:
player = 'X'
else:
player = 'O'
return player
def players_turn(self, player):
"""Changing the players turn."""
return 'X' if player == 'O' else 'O'
def place_move(self, row, column, player):
"""Placing the move, namely putting the players
symbol in the list at the given place."""
self.board[row][column] = player
def start(self):
self.create_board()
player = self.get_random_first_player()
while True:
print(f'It is {player} turn')
self.show_board()
row = int(input('Enter row: '))
column = int(input('Enter column: '))
print()
# row, column = list(
# map(int, input("Enter row and column numbers to fix spot: ").split()))
# print()
self.place_move(row - 1, column - 1, player)
if self.check_win(player):
print(f'{player} wins the game')
break
if self.check_board():
print('We have a draw')
break
player = self.players_turn(player)
print()
self.show_board()
v = TicTacToe(3)
v.start()
Your problem is here:
self.column = []
for _ in range(self.sz):
self.column.append('-')
self.board.append(self.column)
Each column in the board is actually a reference to self.column. Therefore, when one of the members of any of the columns changes, that member changes in all columns.
To get around this, use
self.board.append(self.column.copy())
Now, each column of your board is a separate object.
My tic-tac-toe doesn't seem to work properly. I have tried various things, but nothing changes.
You can run the script yourself, just to see that every time after asking the player's move the game makes a vertical line of X's at the desired column and ends there.
It's probably a problem with my implementation of minimax or the computedMove function, although i cannot locate any errors in there.
# Boardsize initialization
boardSize = 0
# Board initialization
board = []
person = 'X'
ai = 'O'
#This variable indicates the player who has their turn at the moment.
currentPlayer = ''
# This shows the board.
for n in range (boardSize):
for m in range (boardSize):
print (" - "),
print ("\n")
#Checking if somebody won (only horizontal or vertical)
def winLine(line, letter):
return all(n == letter for n in line)
#New list from diagonals
def winDiagonal(board):
return (board[n][n] for n in range (boardSize))
#The function universally checks whether somebody has won, or not.
def checkWinner (board):
#Liczenie wolnych pol
openSpots = 0
for n in range(boardSize):
for m in range(boardSize):
if board[n][m] == '0':
openSpots += 1
#Transposition of the board, so it's possible to use winline() here
for letter in (person, ai):
transPos = list(zip(*board))
#Horizontal check
if any(winLine(row, letter) for row in board):
return letter
#Vertical check
elif any (winLine(col, letter) for col in transPos):
return letter
#Diagonal check
elif any (winLine(winDiagonal(dummy), letter) for dummy in (board, transPos)):
return letter
elif openSpots == 0: return 'tie'
else: return 'N/A'
#This function returns the player's move
def playerMove (row, col):
#Checking if the field is clear
if board[row][col] == '0':
board[row-1][col-1] = person
else:
print('You cannot make that move.')
#Minimax constants
plusInf = float('inf')
minusInf = float('-inf')
#Lookup table for minimax scores
scores = {
'X': 10,
'O': -10,
'None': 0
}
#Minimax itself
def minimax(baord, depth, maximizes):
#Checking whether anybody has won
res = checkWinner(board)
if (res != 'N/A'):
return scores[res]
#Maximizing player
if maximizes:
minmaxBoard = board.copy()
maxTarget = minusInf
for n in range(boardSize):
for m in range(boardSize):
if minmaxBoard[n][m] == '0':
minmaxBoard[n][m] = ai
score = minimax(minmaxBoard, depth + 1, False)
maxTarget = max(score, maxTarget)
return maxTarget
#Minimizing player
else:
minTarget = plusInf
minmaxBoard = board.copy()
for n in range(boardSize):
for m in range(boardSize):
if minmaxBoard[n][m] == '0':
minmaxBoard[n][m] = person
score = minimax(minmaxBoard, depth + 1, True)
minTarget = min(score, minTarget)
return minTarget
#The computer uses this function to make its move
def computedMove():
computedTarget = minusInf
for n in range(boardSize):
for m in range(boardSize):
newBoard = board.copy()
if newBoard[n][m] == '0':
newBoard[n][m] = ai
score = minimax(newBoard, 0, False)
if score > computedTarget:
computedTarget = score
move = (n,m)
board[move[0]][move[1]] = ai
# Getting input for the player's move
def getPlayerMove():
res = input('Please type in your move on the form \"x y\", x being the number of the column and y the number of the row of your choosing.\n')
col, row = res.split(" ")
row = int(row)
col = int(col)
move = (row, col)
return move
# Drawing the board
def drawBoard():
for n in range(boardSize):
for m in range(boardSize):
if board[n][m] == '0':
print(' - ', end='')
else:
print(' '+board[n][m]+' ', end='')
print('\n')
# Current state of the game, False at first
playing = False
#The game loop
while True:
currentPlayer = person
boardSize = int(input("Please enter the size of the board. (one sie)\n"))
board = [['0']*boardSize]*boardSize
print("You go first.")
playing = True
while playing:
if currentPlayer == person:
drawBoard()
move = getPlayerMove()
playerMove(move[0]-1, move[1]-1)
if checkWinner(board) == person:
drawBoard()
print("Yaay, you won!")
playing = False
else:
if checkWinner(board) == 'tie':
drawBoard()
print('It\'s a tie!')
break
else:
currentPlayer = ai
if currentPlayer == ai:
computedMove()
if checkWinner(board) == ai:
drawBoard()
print('You lose!')
playing = False
else:
if checkWinner(board) == 'tie':
drawBoard()
print('It\'s a tie!')
break
else:
currentPlayer = person
if not input('Do you want to play again?').lower().startswith('y'):
break
Check out this statement:
board = [['0']*boardSize]*boardSize
You're essentially creating a list of references to the same list boardSize times. That's why when you're assigning something to board[i][j] element, it gets assigned to j-th elements of all rows (from board[0] to board[len(board)]), because all rows are referencing the same list.
Use this instead:
board = [['0'] * boardSize for _ in range(boardSize)]
There are other issues with this code though. I'm sure you're decrementing your (x, y) indexes multiple times, for example. I didn't check it further.
I am making a basic game of memory matching. The idea is that by user input a board can be created with cards. Those cards all have a match somewhere on the board. Basically the input is the dimensions of the board and then coordinates to identify possible matches. However everytime I enter input even if valid the code seems to defer to invalid input. However when I enter two identical coordinates the part of my code will execute for that case. I am not sure what is going on here.
import random
class Card():
"""Card object """
def __init__(self, val):
self.val = val
self.face = False
def isFaceUp(self):
"""check to see if the cards face up"""
return self.face
def getVal(self):
return self.val
def makeFaceUp(self):
self.face = True
def __str__(self):
return ", ".join(("Value: ", str(self.val), "Face: ", str(self.face)))
class Deck():
def __init__(self, pairs):
self._pairs = pairs
self._cards = []
for cards in range(self._pairs):
card1 = Card(cards + 1)
self._cards.append(card1)
card2 = Card(cards+1)
self._cards.append(card2)
def deal(self):
if len(self) ==0:
return None
else:
return self._cards.pop(0)
def shuffle(self):
random.shuffle(self._cards)
def __len__(self):
return len(self._cards)
class Game():
def __init__(self, rows, columns):
self._deck = Deck((rows*columns)//2)
self._rows = rows
self._columns = columns
self._board = []
for row in range(self._rows):
self._board.append([0] * self._columns)
self.populateBoard()
def populateBoard(self):
"""Puts all cards in the board random"""
self._deck.shuffle()
for columns in range(self._columns):
for rows in range(self._rows):
self._board[rows][columns] = self._deck.deal()
def revealBoard(self):
"""checks the values on the board making sure theres pairs"""
for rows in range(self._rows):
for columns in range(self._columns):
print(str(self._board[rows][columns].getVal()) + \
" ", end="")
print("")
def displayGame(self):
"""Displays the game in a 2d list"""
for rows in range(self._rows):
for columns in range(self._columns):
if self._board[rows][columns].isFaceUp() == False:
print("*", end = "")
else:
print(str(self._board[rows][columns].getVal() + \
" ", end = ""))
print("")
def play(self):
"""Allows the game to play setting the cards into a double list """
while not self.isGameOver():
self.displayGame()
c1 = input("Enter coordinates, (row, column) of card: ")
c2 = input("Enter the coordinates of match: ")
newC1 = list(map(int, c1.split()))
newCard1 = self._board[(newC1[0])-1][(newC1[1])-1] #Get value here???
newC2 = list(map(int, c2.split()))
newCard2 = self._board[(newC2[0])-1][(newC2[1])-1]
try:
if newCard1 != newCard2:
print("Not a pair", "Found: ",newCard1, "at", "(" + newC1, ", ", newC2, ")")
elif newC1 == newC2:
print("Identical Coordinate Entery")
elif newCard1.getVal() == newCard2.getVal():
self._board[newC1[0]-1][newC1[1]-1].makeFaceUp()
self._board[newC2[0]-1][newC2[1]-1].makeFaceUp()
print("pair found")
except:
print("invalid input")
print("Game Over")
self.displayGame
def isGameOver(self):
"""Test to determine if all the cards are facing up and revelied the game will be over"""
for rows in range(self._rows):
if not all(card.isFaceUp() for card in self._board[rows]):
return False
return True
def main():
while True:
# Force user to enter valid value for number of rows
while True:
rows = input("Enter number of rows ")
if rows.isdigit() and ( 1 <= int(rows) <= 9):
rows = int(rows)
break
else:
print (" ***Number of rows must be between 1 and 9! Try again.***")
# Adding *** and indenting error message makes it easier for the user to see
# Force user to enter valid value for number of columns
while True:
columns = input("Enter number of columns ")
if columns.isdigit() and ( 1 <= int(columns) <= 9):
columns = int(columns)
break
else:
print (" ***Number of columns must be between 1 and 9! Try again.***")
if rows * columns % 2 == 0:
break
else:
print (" ***The value of rows X columns must be even. Try again.***")
game = Game(rows, columns)
game.play()
if __name__ == "__main__":
main()
Any help is appreciated and if anyone would need to see other parts of my code simply ask I'd be happy to put them in there if it will help finding this error.
Edit: Went ahead and added the whole code for testing.
Okay, so from what I could deduce, the error is your very broad error handling in Game.play(). It checks for any error, but when you remove that statement, you could see that you were trying to add a list to a string. To fix that, add in:
newC1 = "(" + ", ".join (list (map (str, newC1))) + ")"
Do the same for newC2. This just changes the lists into strings that look like coordinates.
Been trying to learn python and therefore using a pre-made minesweeper and the exercise was to make a class of some functions. But i seem to fet the error:
line 66, in <listcomp>
values = [grid[r][c] for r,c in self.getneighbors(grid, rowno, colno)]
ValueError: too many values to unpack (expected 2)
And i have no idea how to fix it, tried .items but i have no idea what to do. Hopefully i can get some help/tips. Thank you!
import string
import random
gridsize = int(input('SIZE:'))
numberofmines = int(input('MINES:'))
def setupgrid(gridsize,start,numberofmines):
grid = [['0' for i in range(gridsize)] for i in range(gridsize)]
mines = generatemines(grid,start,numberofmines)
p = omringande(grid)
p.getnumbers(grid)
return (grid,mines)
def showgrid(grid):
gridsize = len(grid)
horizontal = ' '+4*gridsize*'-'+'-'
# Print top column letters
toplabel = ' :) '
for i in string.ascii_lowercase[:gridsize]:
toplabel = toplabel+i+' '
print (toplabel,'\n',horizontal)
# Print left row numbers
for idx,i in enumerate(grid):
row = '{0:2} |'.format(idx+1)
for j in i:
row = row+' '+j+' |'
print (row+'\n'+horizontal)
print ('')
def getrandomcell(grid):
gridsize = len(grid)
a = random.randint(0,gridsize-1)
b = random.randint(0,gridsize-1)
return (a,b)
class omringande(object):
def __init__(self,grid):
self.grid = grid
def getneighbors(self,grid,rowno,colno):
gridsize = len(grid)
row = grid[rowno]
column = grid[rowno][colno]
neighbors = []
for i in range(-1,2):
for j in range(-1,2):
if i == 0 and j == 0: continue
elif -1<rowno+i<gridsize and -1<colno+j<gridsize:
neighbors.append((rowno+i,colno+j))
return (row,column)
def getnumbers(self,grid):
gridsize = len(grid)
for rowno,row in enumerate(grid):
for colno,col in enumerate(row):
if col!='X':
# Gets the values of the neighbors
values = [grid[r][c] for r,c in self.getneighbors(grid, rowno, colno)]
# Counts how many are mines
grid[rowno][colno] = str(values.count('X'))
# Generate mines
def generatemines(grid,start,numberofmines):
gridsize = len(grid)
mines = []
for i in range(numberofmines):
cell = getrandomcell(grid)
while cell==(start[0],start[1]) or cell in mines:
cell = getrandomcell(grid)
mines.append(cell)
for i,j in mines: grid[i][j] = 'X'
return mines
def showcells(grid,currgrid,rowno,colno):
# Exit function if the cell was already shown
if currgrid[rowno][colno]!=' ':
return
# Show current cell
currgrid[rowno][colno] = grid[rowno][colno]
# Get the neighbors if the cell is empty
if grid[rowno][colno] == '0':
for r,c in omringande.getneighbors(grid,rowno,colno):
# Repeat function for each neighbor that doesn't have a flag
if currgrid[r][c] != 'F':
showcells(grid,currgrid,r,c)
def playagain():
choice = input('Play again? (y/n): ')
return choice.lower() == 'y'
def playgame():
currgrid = [[' ' for i in range(gridsize)] for i in range(gridsize)]
showgrid(currgrid)
grid = []
flags = []
helpmessage = "Type the column followed by the row (eg. a5).\nTo put or remove a flag, add 'f' to the cell (eg. a5f)\n"
print (helpmessage)
while True:
while True:
lastcell = str(input('Enter the cell ({} mines left): '.format(numberofmines-len(flags))))
print ('\n\n')
flag = False
try:
if lastcell[2] == 'f': flag = True
except IndexError: pass
try:
if lastcell == 'help':
print (helpmessage)
else:
lastcell = (int(lastcell[1])-1,string.ascii_lowercase.index(lastcell[0]))
break
except (IndexError,ValueError):
showgrid(currgrid)
print ("Invalid cell.",helpmessage)
if len(grid)==0:
grid,mines = setupgrid(gridsize,lastcell,numberofmines)
rowno,colno = lastcell
if flag:
# Add a flag if the cell is empty
if currgrid[rowno][colno]==' ':
currgrid[rowno][colno] = 'F'
flags.append((rowno,colno))
# Remove the flag if there is one
elif currgrid[rowno][colno]=='F':
currgrid[rowno][colno] = ' '
flags.remove((rowno,colno))
else: print ('Cannot put a flag there')
else:
# If there is a flag there, show a message
if (rowno,colno) in flags:
print ('There is a flag there')
else:
if grid[rowno][colno] == 'X':
print ('Game Over\n')
showgrid(grid)
if playagain(): playgame()
else: exit()
else:
showcells(grid,currgrid,rowno,colno)
showgrid(currgrid)
if set(flags)==set(mines):
print ('You Win')
if playagain(): playgame()
else: exit()
playgame()
I keep getting the error when the player is supposed to make their move. Tried using (mentioned in other forums) the ".items" but without success.
I made this memory card matching game for class and feel like I wrote more code than I had to. Is there any way to optimize this? The assignment was just to get a working program but I feel like I need to learn a little bit of optimization.
import random
class Card(object):
""" A card object with value but not suit"""
def __init__(self, value):
"""Creates a card with the given value and suit."""
self.value = value
self.face = 0
def showFace(self):
'''flips the card over'''
self.face = 1
def __str__(self):
"""Returns the string representation of a card."""
return str(self.value)
class Deck(object):
""" A deck containing cards."""
def __init__(self, rows, columns):
"""Creates a full deck of cards."""
self._cards = []
self._rows = rows
self._columns = columns
for value in range(1,(int((self._rows*self._columns)/2))+1):
c = Card(value)
self._cards.append(c)
self._cards.append(c)
def shuffle(self):
"""Shuffles the cards."""
random.shuffle(self._cards)
def deal(self):
"""Removes and returns the top card or None
if the deck is empty."""
if len(self) == 0:
return None
else:
return self._cards.pop(0)
def __len__(self):
"""Returns the number of cards left in the deck."""
return len(self._cards)
def __str__(self):
"""Returns the string representation of a deck."""
self.result = ''
for c in self._cards:
self.result = self.result + str(c) + '\n'
return self.result
class Game(Deck,Card):
'''Runs the memory game'''
def __init__(self, rows, columns):
'''gets rows and columns for the game'''
self._rows = rows
self._columns = columns
def play(self):
'''starts the game'''
self._deck = Deck(self._rows, self._columns)
self._deck.shuffle()
Game.populateBoard(self)
matches = 0
#While the game is not finished
while True:
while True: #The First Card
try:
coor1 = input("Enter coordinates for first card ")
coor1 = coor1.split(' ') # Removes spaces
coor1 = [x for x in coor1 if x != ' '] # puts in list
coor1 = [int(x)-1 for x in coor1 if x == x] # converts
except IndexError:
print('***Invalid coordinates! Try again.***')
except ValueError:
print('***Invalid coordinates! Try again.***')
else:
try: #Check if coordinates are valid
if 0 > coor1[0] or coor1[0] > self._rows\
or 0 > coor1[1] or coor1[1] > self._columns:
print('***Invalid coordinates! Try again.***')
else:
guess1 = self._gameboard[coor1[0]][coor1[1]]
break
except IndexError:
print('***Invalid coordinates! Try again.***')
while True: # The Second Card
try:
coor2 = input("Enter coordinates for second card ")
coor2 = coor2.split(' ') # Removes Spaces
coor2 = [x for x in coor2 if x != ' '] # puts in list
coor2 = [int(x)-1 for x in coor2 if x == x]# converts
except IndexError:
print('***Invalid coordinates! Try again.***')
except ValueError:
print('***Invalid coordinates! Try again.***')
else:
try: #Check if coordinates are valid
if 0 > coor2[0] or coor2[0] > self._rows\
or 0 > coor2[1] or coor2[1] > self._columns:
print('***Invalid coordinates! Try again.***')
else:
guess2 = self._gameboard[coor2[0]][coor2[1]]
break
except IndexError:
print('***Invalid coordinates! Try again.***')
if guess1 == guess2\
and coor2[0]-coor1[0] == 0\
and coor2[1]-coor1[1] == 0:#User enters same input for 1 and 2
print("***That's the same card! Try again.***")
elif guess1 == guess2:
guess1.showFace()
Game.showBoard(self)
matches += 1
if matches == ((self._rows * self._columns)/2):
break
else:
Game.showBoard(self)
print('Not an identical pair. Found',guess1,'at ('+
str(coor1[0]+1)+','+ str(coor1[1]+1)+') and', guess2,
'at ('+str(coor2[0]+1)+','+str(coor2[1]+1)+')')
def populateBoard(self):
'''populates the game board'''
self._gameboard = []
for row in range(self._rows):
self._gameboard.append([0] * self._columns)
for row in range(len(self._gameboard)):
for column in range(len(self._gameboard[row])):
self._gameboard[row][column] = self._deck.deal()
Game.showBoard(self)
def showBoard(self):
'''shows the board'''
for row in self._gameboard:
for card in row:
if card.face == 0:
print('*', end =" ")
else:
print(card, end= " ")
print()
def main():
while True:
# Force user to enter valid value for number of rows
while True:
rows = input("Enter number of rows ")
if rows.isdigit() and ( 1 <= int(rows) <= 9):
rows = int(rows)
break
else:
print (" ***Number of rows must be between 1 and 9! Try again.***")
# Adding *** and indenting error message makes it easier for the user to see
# Force user to enter valid value for number of columns
while True:
columns = input("Enter number of columns ")
if columns.isdigit() and ( 1 <= int(columns) <= 9):
columns = int(columns)
break
else:
print (" ***Number of columns must be between 1 and 9! Try again.***")
if rows * columns % 2 == 0:
break
else:
print (" ***The value of rows X columns must be even. Try again.***")
game = Game(rows, columns)
game.play()
if __name__ == "__main__":
main()
Here is a couple of places the code can be simplified
def populateBoard(self):
'''populates the game board'''
self._gameboard = [self._deck.deal() for row in self._rows
for column in self._columns]
self.showBoard()
and
def showBoard(self):
'''shows the board'''
for row in self._gameboard:
print(*(card if card.face else '*' for card in row))
coor1[0] is computed many times over. Consider assigning the result to a local variable and then using this local variable.