I am writing code to a basic tictactoe game, but I am having trouble with a certain part of my code when trying to get it to break out of the while loop.
I have a list (= board) that contains 10 items = 'space'. The fullBoardCheck function should return a boolean = True when the condition is met, otherwise False.
Within initializeGame, the while loop is set to continue while gameStatus = False. However, once I run the game and fill out 9 spaces of the Board, gameStatus changes to True but it does not break out of the loop.
I cannot find what I am missing, and would appreciate any help you could offer.
Thanks
def placeMarkerO(board, position):
board[position] = 'O'
return board
def takeTurnO(board):
print("It's O's turn!")
wantedPosition = int(input('O, where would you like to go? (1-9): '))
while not spaceCheck(board, wantedPosition):
wantedPosition = int(input('This space is taken. Please choose an EMPTY space: '))
return wantedPosition
def fullBoardCheck(board):
# Returns true or false boolean checking if board is full
return len([x for x in board if x == " "]) == 1
def initializeGame():
board = [' '] * 10
gameStatus = False
while gameStatus == False:
drawBoard(board)
gameStatus = fullBoardCheck(board)
position = takeTurnX(board)
placeMarkerX(board, position)
drawBoard(board)
fullBoardCheck(board)
position = takeTurnO(board)
placeMarkerO(board, position)
You need to change the fullBoardCheck function:
def fullBoardCheck(board):
return len([x for x in board if x == ' ']) == 0
You checked if the len([x for x in board if x == ' ']) == 1, however, you need to check if the length is 0 because you want to return whether the board is completely full.
Good Luck!
Related
the window of game.py I try out some of the python beginner project to practise my sense of coding.
And after i followed the youtube video step by step, i started to debug and play the
TicTacToe. It closed right after i input my first square/input. But i don't understand
where it goes wrong. I just start fighting with coding, i don't mind to get slap right out
of my face right now. Please, go easy on me. It keeps drive me to the wall.
#Player.py ||||
import math
import random
class Player:
def __init__(self,letter):
self.letter = letter
def get_move(self,game):
pass
class RandomComputerPlayer(Player):
def __init__(self, letter):
super().__init__(letter)
def get_move(self,game):
square = random.choice(game.available_moves())
return square
class HumanPlayer(Player):
def __init__(self, letter):
super().__init__(letter)
def get_move(self,game):
valid_square = False
val = None
while not valid_square:
square = input(self.letter + '\'s turn. Input move (0-8):')
try:
val = int(square)
if val not in game.available_moves():
raise ValueError
valid_square = True
except ValueError:
print('Invalid square. Try again.')
return val
And here's game.py:
#game.py ||||
import time
from player import HumanPlayer, RandomComputerPlayer
class TicTacToe:
def __init__(self):
self.board = [' ' for _ in range(9)]
self.current_winner = None
def print_board(self):
for row in [self.board[i*3:(i+1)*3] for i in range(3)]:
print('| ' + ' | ' .join(row) + ' |')
#staticmethod
def print_board_nums():
# 0 | 1 | 2 etc
number_board = [[str(i) for i in range(j*3, (j+1)*3)] for j in range (3)]
for row in number_board:
print('| ' + ' | '.join(row) + ' |')
def available_moves(self):
moves = []
for (i, x) in enumerate(self.board):
if spot == ' ':
moves.append(i)
def empty_squares(self):
return ' ' in self.board
def num_empty_squares(self):
return len(self.available_moves())
def make_move(self, square, letter):
if self.board[square] == ' ':
self.board[square] = letter
if self.winner(square, letter):
self.current_winner = letter
return True
return False
def winner(self, square, letter):
# winner if 3 in a row anywhere, check all of the possiblity
row_ind = square // 3
row = self.board[row_ind*3 : (row_ind + 1) * 3]
if all ([spot == letter for spot in row]):
return True
col_ind = square % 3
column = [self.board[col_ind + i*3] for i in range(3)]
if all ([spot == letter for spot in column]):
return True
# check diagonals
if square % 2 == 0:
diagonal1 = [self.board[i] for i in[0,4,8]]
if all ([spot == letter for spot in diagonal1]):
return True
diagonal2 = [self.board[i] for i in[2,4,6]]
if all ([spot == letter for spot in diagonal2]):
return True
# if all of the possibility not happen
return False
def play(game, x_player, o_player, print_game = True):
# returns the winner of the game! or None for a tie
if print_game:
game.print_board_nums()
letter = 'X'
while game.empty_squares():
if letter == '0':
square = o_player.get_move(game)
else:
square = x_player.get_move(game)
if game.make_move(square, letter):
if print_game:
print(letter + ' makes a move to square {square}')
game.print_board()
print('') # empty line
if game.current_winner:
if print_game:
print(letter + 'wins!')
return letter
letter = 'O' if letter == 'X' else 'X'
# if letter == 'X':
# letter = 'O'
# else:
# letter = 'X'
time.sleep(0.8)
if print_game:
print('It\'s a tie!')
if __name__ == '__main__':
x_player = HumanPlayer('X')
o_player = RandomComputerPlayer('O')
t = TicTacToe()
play(t, x_player, o_player, print_game = True)
When I copied the code and ran it locally on my computer, the traceback showed that there was an error in game.py, specifically in your available_moves() function.
def available_moves(self):
moves = []
for (i, x) in enumerate(self.board):
if spot == ' ':
moves.append(i)
The traceback specifically says, NameError: name 'spot' is not defined, which is true since spot is not defined. I am assuming spot and x are supposed to mean the same here since you are looking at the square on your Tic-Tac-Toe board. Also, this function should probably return moves; otherwise, any other function calling available_moves() is going to get a value of type None instead of the list of available moves.
def available_moves(self):
moves = []
# iterate through all spots on board
for (i, spot) in enumerate(self.board):
# if a spot is empty, add its index to moves
if spot == ' ':
moves.append(i)
return moves
Also I've been absolutely crushing myself against myself playing this game so thanks for that!
Edit: Just noticed after posting this that Tim Roberts basically summed up my answer. Oops.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 2 years ago.
Improve this question
When i was making a simple tic-tac-toe game i suddenly got this error in my winner() function which uses line_winner(line) to find a winner for each row column and diagonal but i don't know why it is throwing "Attribute None type doesn't have have method line.count()" error which i think it shouldn't so i'm attaching whole program so that you can test that and play that game if it works fine and help me out where i was wrong.
from random import randint
def new_board():
"""Generate and return a new empty board"""
board = [[' ', ' ', ' '], [' ', ' ', ' '], [' ', ' ', ' ']]
return board
def square(board, row, column):
"""Return the contents of square (row, column) of board.
The value is either 'O', 'X' or ' '
"""
return board[row][column]
def row(board, row_num):
"""Return the given numbered row (0 - 2) of board"""
if -1 < row_num < 3:
return board[row_num]
return 0
def column(board, column_num):
"""Return the given numbered column (0 - 2) of board"""
col = []
if -1 < column_num < 3:
for rows in range(0, len(board)):
col.append(board[rows][column_num])
return col
return 0
def diagonal(board, diagonal_selector):
"""Return the 3 element diagonal of the board selected by
diagonal_selector, one of TOP_LEFT_BOTTOM_RIGHT or
TOP_RIGHT_BOTTOM_LEFT
"""
diagonals = []
if diagonal_selector == 1:
diagonals.append(board[0][2])
diagonals.append(board[1][1])
diagonals.append(board[2][0])
elif diagonal_selector == 0:
diagonals.append(board[0][0])
diagonals.append(board[1][1])
diagonals.append(board[2][2])
else:
return diagonals
def empty_squares(board):
"""Return a list of the empty squares in the board, each as
a (row, column) tuple"""
indexes = []
for rows in range(0, len(board)):
for col in range(0, len(board[0])):
if square(board, rows, col) == ' ':
indexes.append((rows, col))
return indexes
def line_winner(line):
"""Return 'O' or 'X' if all elements in the 3-element list line
are the same and non-blank. Otherwise return None"""
if line.count(line[0]) == 3 and line[0] != ' ':
return line[0]
return None
def winner(board):
"""Return 'O' or 'X' if either of those has won the game.
Otherwise return None. It is assumed there can be only a
single winning line."""
game_winner = None
for index in range(0, len(board)):
# Check rows
if line_winner(row(board, index)) is not None:
game_winner = line_winner(row(board, index))
# check columns
elif line_winner(column(board, index)) is not None:
game_winner = line_winner(column(board, index))
# check diagonals
elif index < 2:
if line_winner(diagonal(board, index)) is not None:
game_winner = line_winner(diagonal(board, index))
# final winner
return game_winner
def game_over(board):
"""Given a board state return true iff there's a winner or
if the game is drawn."""
# there is a winner
if winner(board):
return True
# every square is filled
elif len(empty_squares(board)) == 0:
return True
return False
def game_result(board):
"""Return 'Won by O', 'Won by X' or 'Draw' according to the
state of board. It is assume the game is over."""
if winner(board) == 'X':
print("Won by player X")
elif winner(board) == 'O':
print("Won by player O")
else:
print("Draw")
def make_human_move(current_player, board):
"""Given a board state and the human piece ('O' or 'X')
ask the player for a location to play in. Repeat until a
valid response is given. Then make the move, i.e., update
the board by setting the chosen square to the player's piece.
"""
if current_player == 'X':
print("X's move")
if current_player == 'O':
print("O's move")
move = input("Enter row and column [0-2] ")
rows = int(move[0])
col = int(move[-1])
if -1 < int(move[0]) < 3 and -1 < int(move[-1]) < 3 and square(board, rows, col) == ' ':
board[rows][col] = current_player
else:
print("Illegal move try again")
make_human_move(current_player, board)
def play_one_turn(board, current_player, human_player):
"""Given a board state and the current
player ('O' or 'X'), play one move"""
if current_player == human_player:
make_human_move(current_player, board)
else:
make_computer_move(current_player, board)
def other_player(player):
"""Return X if player is O else return O"""
if player == 'X':
return 'O'
elif player == 'O':
return 'X'
return ' '
def get_O_or_X():
"""Ask the human if they want to play O or X and return their
choice"""
choice = input("Would you like to play O or X ?")
if choice == 'X' or choice == 'O':
return choice
else:
get_O_or_X()
def play_game(human_player, board):
"""Play until a win or a draw"""
current_player = human_player
while game_over(board) is False:
display(board)
play_one_turn(board, current_player, human_player)
current_player = other_player(current_player)
game_result(board)
def make_computer_move(current_player, board):
"""Given a board state and the computer piece ('O' or 'X')
choose a square for the computer to play in and
make the move (i.e., update the board accordingly).
"""
candidates = empty_squares(board)
choice = randint(0, len(candidates) - 1)
row, column = candidates[choice]
print("Computer plays at ({},{})".format(row, column))
board[row][column] = current_player
def display(board):
"""Display the given board"""
separator = '+---+---+---+'
print(separator)
for row in board:
print('|', end='')
for col in row:
print(' {} |'.format(col), end='')
print('\n' + separator)
print()
def main():
"""Play a game of noughts and crosses"""
board = new_board()
human_player = get_O_or_X()
try:
play_game(human_player, board)
display(board)
except ValueError:
print("The program has encountered an error and needs to die. Bye.")
main()
diagonal() doesn't return the diagonals list when diagonal_selector is 1 or 2. The return diagonals statement should not be in else:, it should be done always.
def diagonal(board, diagonal_selector):
"""Return the 3 element diagonal of the board selected by
diagonal_selector, one of TOP_LEFT_BOTTOM_RIGHT or
TOP_RIGHT_BOTTOM_LEFT
"""
diagonals = []
if diagonal_selector == 1:
diagonals.append(board[0][2])
diagonals.append(board[1][1])
diagonals.append(board[2][0])
elif diagonal_selector == 0:
diagonals.append(board[0][0])
diagonals.append(board[1][1])
diagonals.append(board[2][2])
return diagonals
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.
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!
I have made a sonar finding game and some functions for it. I have put these functions into a class and now I want to call them so I can play the game, however when I define the class call it now returns things to me like game.getNewBoard() takes 0 positional arguments and tells me I have given it one when I have not and so on. Any help would be appreciated.
# Sonar
import random
import sys
class OceanTreasure:
def drawBoard(board):
# Draw the board data structure.
hline = ' ' # initial space for the numbers down the left side of the board
for i in range(1, 6):
hline += (' ' * 9) + str(i)
# print the numbers across the top
print(hline)
print(' ' + ('0123456789' * 6))
print()
# print each of the 15 rows
for i in range(15):
# single-digit numbers need to be padded with an extra space
if i < 10:
extraSpace = ' '
else:
extraSpace = ''
print('%s%s %s %s' % (extraSpace, i, getRow(board, i), i))
# print the numbers across the bottom
print()
print(' ' + ('0123456789' * 6))
print(hline)
def getRow(board, row):
# Return a string from the board data structure at a certain row.
boardRow = ''
for i in range(60):
boardRow += board[i][row]
return boardRow
def getNewBoard():
# Create a new 60x15 board data structure.
board = []
for x in range(60): # the main list is a list of 60 lists
board.append([])
for y in range(15): # each list in the main list has 15 single-character strings
# I thought about using different char to make it more readble?? Admit it, it looks dull with just these ~
if random.randint(0, 1) == 0:
board[x].append('~')
else:
board[x].append('~')
return board
def getRandomChests(numChests):
# Create a list of chest data structures (two-item lists of x, y int coordinates)
chests = []
for i in range(numChests):
chests.append([random.randint(0, 59), random.randint(0, 14)])
return chests
def isValidMove(x, y):
# Return True if the coordinates are on the board, otherwise False.
return x >= 0 and x <= 59 and y >= 0 and y <= 14
def makeMove(board, chests, x, y):
# Change the board data structure with a sonar device character. Remove treasure chests
# from the chests list as they are found. Return False if this is an invalid move.
# Otherwise, return the string of the result of this move.
if not isValidMove(x, y):
return False
smallestDistance = 100 # any chest will be closer than 100.
for cx, cy in chests:
if abs(cx - x) > abs(cy - y):
distance = abs(cx - x)
else:
distance = abs(cy - y)
if distance < smallestDistance: # we want the closest treasure chest.
smallestDistance = distance
if smallestDistance == 0:
# xy is directly on a treasure chest!
chests.remove([x, y])
return 'You have found a sunken treasure chest!'
else:
if smallestDistance < 10:
board[x][y] = str(smallestDistance)
return 'Treasure detected at a distance of %s from the sonar device.' % (smallestDistance)
else:
board[x][y] = 'O'
return 'Sonar did not detect anything. All treasure chests out of range.'
def enterPlayerMove():
# Let the player type in her move. Return a two-item list of int xy coordinates.
print('Where do you want to drop the next sonar device? (0-59 0-14) (or type quit)')
while True:
move = input()
if move.lower() == 'quit':
print('Thanks for playing!')
sys.exit()
move = move.split()
if len(move) == 2 and move[0].isdigit() and move[1].isdigit() and isValidMove(int(move[0]), int(move[1])):
return [int(move[0]), int(move[1])]
print('Enter a number from 0 to 59, a space, then a number from 0 to 14.')
def playAgain():
# This function returns True if the player wants to play again, otherwise it returns False.
print('Do you want to play again? (yes or no)')
return input().lower().startswith('y')
print('S O N A R !')
print()
while True:
# game setup
game=OceanTreasure()
sonarDevices = 20
theBoard = game.getNewBoard()
theChests = getRandomChests(3)
drawBoard(theBoard)
previousMoves = []
while sonarDevices > 0:
# Start of a turn:
# sonar device/chest status
if sonarDevices > 1: extraSsonar = 's'
else: extraSsonar = ''
if len(theChests) > 1: extraSchest = 's'
else: extraSchest = ''
print('You have %s sonar device%s left. %s treasure chest%s remaining.' % (sonarDevices, extraSsonar, len(theChests), extraSchest))
x, y = enterPlayerMove()
previousMoves.append([x, y]) # we must track all moves so that sonar devices can be updated.
moveResult = makeMove(theBoard, theChests, x, y)
if moveResult == False:
continue
else:
if moveResult == 'You have found a sunken treasure chest!':
# update all the sonar devices currently on the map.
for x, y in previousMoves:
makeMove(theBoard, theChests, x, y)
drawBoard(theBoard)
print(moveResult)
if len(theChests) == 0:
print('You have found all the sunken treasure chests! Congratulations and good game!')
break
sonarDevices -= 1
if sonarDevices == 0:
print('We\'ve run out of sonar devices! Now we have to turn the ship around and head')
print('for home with treasure chests still out there! Game over.')
print(' The remaining chests were here:')
for x, y in theChests:
print(' %s, %s' % (x, y))
if not playAgain():
sys.exit() #I thought this is a better way than just break or make false, correct me if I am wrong
Every class method in Python receives the instance it is called from as the first parameter automatically. That means, that the first parameter is always self:
def drawBoard(self, board):