there a problem with add AI + r.radint(0, 99).
i want a user to input who to attack and if a user type in the wrong input then prompt invalid input and try again using while loop, if a user type in AI99 (for example) that in the list on a ai unit then it will break and continue the program.
i want to use this statement under the input ("who do you want attack?")
# Prompts the user who he/she wants to attack.
opponent_name_to_attack = input("Who do you want attack? ").upper()
Main:
import gameplayerteam
import gamechar
import random as r
import gamelogger
player_units = None # A GamePlayerTeam object which comprises of 3 Human players
ai_units = None # A GamePlayerTeam object which comprises of 3 AI players
ai_units_player_id = []
ai_character_class_list = ['warrior', 'tanker']
def main():
gamelogger.prepares_log_folder()
gamelogger.print_and_log_game_message("Welcome to battle game!")
setup_human_unit()
setup_ai()
game_is_over = False
player_turn_of_attack = 'human' # Possible values: 'human', 'ai'
previous_player = None
curr_player = None
opponent_player_team = None
while not game_is_over:
gamelogger.print_and_log_game_message("============= Human Unit =============")
for i in range(len(player_units.players)):
gamelogger.print_and_log_game_message(player_units.players[i])
gamelogger.print_and_log_game_message("======================================\n")
gamelogger.print_and_log_game_message("============= AI Unit ================")
for i in range(len(ai_units.players)):
gamelogger.print_and_log_game_message(ai_units.players[i])
gamelogger.print_and_log_game_message("======================================\n")
#print("Is Game Over? : " + str(game_is_over))
if player_turn_of_attack == 'human':
#Gets the current human player's GameCharacter object
curr_player = player_units.get_current_player()
gamelogger.print_and_log_game_message("Now the {0} is playing".format(curr_player.name))
# Prompts the user who he/she wants to attack.
opponent_name_to_attack = input("Who do you want attack? ").upper()
# Sets the current_player_id of the matching AI Player's name that the user input
ai_units.set_player_id(opponent_name_to_attack)
# Sets the predicted next (name of) AI player after current AI player's turn ends
ai_units.predict_next_player()
# Assigns the AI Team (GamePlayerTeam object) as the opponent_player_team
opponent_player_team = ai_units
# Let's search for the GameCharacter object with the same name as opponent_to_attack
opponent_to_attack = ai_units.get_player_by_name(opponent_name_to_attack)
# Update the player_turn_of_attack value to ai so that the next turn will be AI
player_turn_of_attack = 'ai'
else:
gamelogger.print_and_log_game_message("Now the AI is playing")
# Sets the predicted next (name of) human player after current human player's turn ends
player_units.predict_next_player()
# Update the opponent_name_to_attack with previous_player
opponent_name_to_attack = previous_player.name
# Assigns the Human Team (GamePlayerTeam object) as the opponent_player_team
opponent_player_team = player_units
# Let's search for the GameCharacter object with the same name as opponent_to_attack
opponent_to_attack = previous_player
# Update the player_turn_of_attack value to human so that the next turn will be human
player_turn_of_attack = 'human'
# Update the curr_player_id to the next human player id
#player_units.set_next_player_id()
gamelogger.print_and_log_game_message("Attacking " + opponent_name_to_attack)
curr_player.attack_target(opponent_to_attack)
curr_player.check_rank()
#opponent_player_team.set_next_player_id()
#Checks the opponent player's HP
if opponent_to_attack.hp <= 0:
#Removes this opponent_to_attack from the team
opponent_player_team.remove_player(opponent_to_attack)
opponent_player_team.set_next_player_id()
# The next player in the opponent team will be switched over as the
# opponent_to_attack as our previous opponent player has been disqualified
opponent_to_attack = opponent_player_team.get_current_player()
else:
# We will simply set the curr_player_id to the next (predicted) player
opponent_player_team.set_next_player_id()
# Update the value of curr_player and the previous_player
previous_player = curr_player
curr_player = opponent_to_attack
game_is_over = check_has_game_over()
gamelogger.print_and_log_game_message("===========================")
gamelogger.print_and_log_game_message("Game is over")
def setup_human_unit():
global player_units
player_units = gameplayerteam.GamePlayerTeam('human')
for i in range(3):
char_type = input("Enter Character class [warrior/tanker]: ")
char_name = input("Enter character name: ")
player_units.add_player(char_type, char_name)
def setup_ai():
global ai_units
#Creates a GamePlayerTeam object (with no players first)
ai_units = gameplayerteam.GamePlayerTeam('ai')
for ai_player_count in range(3):
ai_character_class_list.reverse()
random_num = r.choice(ai_character_class_list)
ai_units.add_player(random_num, 'AI'+ str(ai_player_count+r.randint(0, 99)))
#ai_units.append(gamechar.GameCharacter(random_num, 'AI'+ str(ai_player_count+1) ))
def check_has_game_over():
is_game_over = False
# In the entire game, there's only 1 player left (whether it is an AI or human)
if ai_units.check_are_all_players_defeated():
is_game_over = True
elif player_units.check_are_all_players_defeated():
is_game_over = True
else:
is_game_over = False
return is_game_over
main()
gameplayerteam:
import gamechar
class GamePlayerTeam:
def __init__(self, player_type):
self.players = []
self.type_of_player = player_type #Possible value: 'ai', 'human'
self.all_players_defeated = False #Keeping track whether all units in this team are defeated
self.curr_player_id = 0 # Up to the last index of the players list
self.next_player_name = ''
def add_player(self, char_type, char_name):
#Adding a particular unit / player to the team
self.players.append(gamechar.GameCharacter(char_type, char_name))
def remove_player(self, player_to_remove):
# Removes a particular GameCharacter object from the players list
self.players.remove(player_to_remove)
def remove_player_by_id(self, player_id_to_remove):
# Remarks: This method will only likely be used by a Human GameCharacter
# Removes a particular GameCharacter object from the players list with a matching
# player_id_to_remove value
self.players.pop(player_id_to_remove)
def get_player_by_id(self, input_player_id):
# Remarks: This method will only likely be used by a Human GameCharacter
# Returns a particular GameCharacter object from the players list using the provided
# input_player_id value
return self.players[input_player_id]
def set_player_id(self, player_name):
#Sets the id of the player with the matching name from the players list
index = 0
while index < len(self.players):
if self.players[index].name == player_name.upper():
# Targeted Player found, stops executing the loop immediately
break
# Increments index by 1 as target not yet found
index += 1
# Update the curr_player_id to the index that we found
self.curr_player_id = index
def get_player_by_name(self, input_player_name):
# Returns a particular GameCharacter object by searching the matching GameCharacter object
# with the same name as input_player_name through the players list
player = None
for i in range(len(self.players)):
if input_player_name.upper() == self.players[i].name:
player = self.players[i]
break
return player
def get_current_player(self):
# Let's first validate whether if our players list is empty or not
if len(self.players) == 0:
# Because our players list is empty, this function will just simply
# return an empty object
return None
else:
# Returns a particular GameCharacter object from the players list
return self.players[self.curr_player_id]
def predict_next_player(self):
# Looks for the next player, and then update the curr_player_id to the matching next player's id
if self.curr_player_id == len(self.players) - 1:
# Reset the human player id as 0 to be the next human player id
self.next_player_name = self.players[0].name
else:
self.next_player_name = self.players[self.curr_player_id + 1].name
def set_next_player_id(self):
index = 0
while index < len(self.players):
if self.players[index].name == self.next_player_name:
# Targeted Player found, stops executing the loop immediately
break
#Increments index by 1 as target not yet found
index += 1
#Update the curr_player_id to the index that we found
self.curr_player_id = index
def check_are_all_players_defeated(self):
if len(self.players) == 0:
self.all_players_defeated = True
else:
self.all_players_defeated = False
return self.all_players_defeated
gamechar:
import random as r
import gamelogger
class GameCharacter:
def __init__(self, char_type, char_name):
self.type = ""
self.name = char_name
self.hp = 100
self.attack = 0
self.defence = 0
self.xp = 0
self.rank = 1
self.coin = 0
if char_type == 'warrior':
self.setup_warrior()
elif char_type == 'tanker':
self.setup_tanker()
def setup_warrior(self):
self.type = 'warrior'
self.attack = r.randint(5, 20)
self.defence = r.randint(1, 10)
def setup_tanker(self):
self.type = 'tanker'
self.attack = r.randint(1, 10)
self.defence = r.randint(5, 15)
def attack_target(self, target):
damage = self.attack - target.defence + r.randint(-5, 10)
target.hp -= abs(damage)
self.xp += abs(damage)
target.xp += target.defence
target.xp = round(target.xp, 1)
self.coin += target.xp
if damage > 10:
target.xp *= 1.2
# Rounds off (up/down) target.xp to 1 decimal point if it has a value with 2
# or more decimal points
target.xp = round(target.xp, 1)
elif damage <= 0:
target.xp *= 1.5
# Rounds off (up/down) target.xp to 1 decimal point if it has a value with 2
# or more decimal points
target.xp = round(target.xp, 1)
gamelogger.print_and_log_game_message("******************************************")
gamelogger.print_and_log_game_message("[Game Message] {0} attacked {1} with damage {2}: +{2}EXP".format(self.name, target.name, round(abs(damage),1) ) )
# print("Damage to target: {0}".format(damage))
gamelogger.print_and_log_game_message("******************************************")
def check_rank(self):
if self.xp >= 100:
self.rank += 1 # Promote rank by 1
self.xp -= 100 # Reduce the xp by 100
gamelogger.print_and_log_game_message("{0} have been promoted to Rank {1} ".format(self.name, self.rank) )
def __str__(self):
text = "{} {}: HP:{}, ATK:{}, DEF:{}, EXP:{}, RANK: {}".format(self.type, self.name, self.hp, self.attack, self.defence, self.xp, self.rank)
return text
Related
So I am creating a card game in python using classes. I got it all set up to the point where it should work. But when I ran it it just simply never stops running. I am not really sure how to make this code minimal and reproduceable, because I do not know where the issue is at.
Here is the code i have written.
It has to be in the play_uno() class object.
""" UNO Simulator """
import random
class card():
def __init__(self, value, color, wild):
'''
An UNO deck consists of 108 cards, of which there are 76 Number cards,
24 Action cards and 8 Wild cards. UNO cards have four color "suits",
which are red, yellow, blue and green.
'''
self.card_nums = ['0','1','2','3','4','5','6','7','8','9']
self.card_colors = ['red','blue','green','yellow']
self.spec_cards = ['draw2','reverse','skip']
self.wild_cards = ['wild','wild_draw_4']
if wild == False:
self.value = self.card_nums[value]
self.color = self.card_colors[color]
elif wild == 'special':
self.value = self.spec_cards[value]
self.color = self.card_colors[color]
elif wild == True:
self.value = self.wild_cards[value]
self.color = None
class generate_deck():
def __init__(self):
self.number_cards = self.get_num_cards()
self.special_cards = self.get_special_cards()
self.wild_cards = self.get_wild_cards()
self.cards = self.number_cards + self.special_cards + self.wild_cards
random.shuffle(self.cards)
def get_num_cards(self):
# only one zero per color
with_zeroes = [card(i,j,False) for i in range(10) for j in range(4)]
no_zeroes = [card(i,j,False) for i in range(1,10) for j in range(4)]
return no_zeroes + with_zeroes
def get_wild_cards(self):
wild_draw4s = [card(i,None,True) for i in range(2) for x in range(2)]
wilds = [card(i,None,True) for i in range(2) for x in range(2)]
return wilds + wild_draw4s
def get_special_cards(self):
return [card(i,j,'special') for i in range(3) for j in range(4) for x in range(2)]
class player():
def __init__(self, name):
self.wins = 0
self.name = name
self.cheater = False
self.cards = ''
self.turn = 0
self.uno = 0
class play_uno():
def __init__(self, num_players = 3, num_cheaters = 0, cards_per_player = 5):
# get started
self.rules = 'default'
self.status = 'ongoing'
self.deck = generate_deck().cards
self.played_cards = []
self.dro = 0
self.direction = 0
self.top_card = self.deck.pop() # random card as first card to play on
self.tot_turns = 0
# generate players, make cheaters later
self.players = [player('player' + str(i)) for i in range(num_players + num_cheaters)]
# give each player 7 cards to start
for _player_ in self.players:
_player_.cards = [self.draw_card() for i in range(cards_per_player)]
# start playing turns in order
# do not know how to reverse yet
"""
Right now it is endless for some reason.
"""
while self.status == 'ongoing':
for _player in self.players:
self.turn(_player)
def draw_card(self):
# draws random card from deck instead of card on top
if len(self.deck) == 0:
self.re_shuffle()
self.dro += 1
return self.deck.pop()
def re_shuffle(self):
self.deck = self.played_cards
random.shuffle(self.deck)
self.played_cards = []
return self.deck, self.played_cards
def play_card(self, player_cards, _card):
self.top_card = _card
return player_cards.remove(_card), self.played_cards.append(_card), self.top_card
def game_over(self, winner):
winner.wins += 1
self.game_status = 'over'
def turn(self, _player):
played = False
# check if someone played wild card last turn
if self.top_card.value in card(1,2,None).wild_cards:
self.top_card.color = random.choice(card.card_colors)
if self.top_card.value == 'wild_draw_4':
_player.cards += [self.draw_card() for i in range(4)]
self.tot_turns += 1
return _player
# check for special cards
elif self.top_card.value in card(1,2,None).spec_cards:
if self.top_card.value == 'draw2':
_player.cards += [self.draw_card() for i in range(4)]
self.tot_turns += 1
return _player
# for now we are treating reverse cards like skips
elif self.top_card.value == 'reverse' or self.top_card.value == 'skip':
played = True
self.tot_turns += 1
return _player
# If its a normal card, or regular wild
if played == False:
for _card in _player.cards:
if _card.color == self.top_card.color:
self.play_card(_player.cards, _card)
played = True
break
elif _card.value == self.top_card.value:
self.play_card(_player.cards, _card)
played = True
break
# if the player cannot play at all
# rn they just move on if they have to draw,
# cant play the card they just drew.
if played == False:
_player.cards += [self.draw_card()]
played = True
self.tot_turns += 1
# check if the player won or not
if len(_player.cards) == 0:
self.game_over(_player)
elif len(_player.cards) == 1:
_player.uno += 1
return _player.cards
In the function turn in the play_uno class you are checking for certain wild/special cards. If the value is reverse, for example, the function hits a return _player line which ends the execution of the function and the player is unable to play another card.
Move the return statements to the end of the function if you want to ensure the rest of the code is run.
I did not run the code, but I think you should test a player's number of cards before drawing again. Like this:
if len(_player.cards) == 0:
self.game_over(_player)
elif len(_player.cards) == 1:
_player.uno += 1
if played == False:
_player.cards += [self.draw_card()]
played = True
self.tot_turns += 1
Or are the rules of this game different? I sincerely don't remember them anymore.
The while loop at the end of play_uno's __init__ checks for status, which never changes.
The loop calls turn on each of players every iteration. You must change status somewhere or you must put an if ...: break in the while loop (e.g. if not _player.cards).
EDIT: It appears that you meant self.status instead of self.game_status, in game_over. Try changing game_status to status there.
I'm working on a Mancala game where players get to play against an AI. the code is complete, the Mancala game functionality is found within the Mancala_helpers, the AI algorithm is a MinMax tree and is found in the MinMax file, and finally the game itself. everything runs fine except for when the AI plays, if the AI starts the game immediately ends, it moves all the rocks from its pits in one round. and if I start I can only play one move before it does the same. I cannot understand what's happening, at first, I thought maybe I had a problem within the function of mancala helpers where they did not switch turns properly and the AI kept playing. but I ran multiple tests and that part is working fine. I cant identify the issue, help, please. if anyone also has suggestions for a better evaluation function then that would be great. thanks
--------------------------Mancala helpers--------------
# TODO: implement pad(num)
# Return a string representation of num that is always two characters wide.
# If num does not already have two digits, a leading "0" is inserted in front.
# This is called "padding". For example, pad(12) is "12", and pad(1) is "01".
# You can assume num is either one or two digits long.
def pad(num: int) -> str:
x = str(num)
if len(x) > 1:
return x
else:
return "0"+x
# TODO: implement pad_all(nums)
# Return a new list whose elements are padded versions of the elements in nums.
# For example, pad_all([12, 1]) should return ["12", "01"].
# Your code should create a new list, and not modify the original list.
# You can assume each element of nums is an int with one or two digits.
def pad_all(nums: list) -> list:
x = []
for i in nums:
x.append(pad(i))
return x
# TODO: implement initial_state()
# Return a (player, board) tuple representing the initial game state
# The initial player is player 0.
# board is list of ints representing the initial mancala board at the start of the game.
# The list element at index p should be the number of gems at position p.
def initial_state() -> tuple:
return (0, [4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 0])
# TODO: implement game_over(state)
# Return True if the game is over, and False otherwise.
# The game is over once all pits are empty.
# Your code should not modify the board list.
# The built-in functions "any" and "all" may be useful:
# https://docs.python.org/3/library/functions.html#all
def game_over(state: tuple) -> bool:
lst = state[1]
if (lst[0] == lst[1] == lst[2] == lst[3] == lst[4] == lst[5] == 0) or (lst[7] == lst[8] == lst[9] == lst[10] == lst[11] == lst[12] == 0):
return True
else:
return False
# TODO: implement valid_actions(state)
# state is a (player, board) tuple
# Return a list of all positions on the board where the current player can pick up gems.
# A position is a valid move if it is one of the player's pits and has 1 or more gems in it.
# For example, if all of player's pits are empty, you should return [].
# The positions in the returned list should be ordered from lowest to highest.
# Your code should not modify the board list.
def valid_actions(state: tuple) -> list:
actions = []
lst = state[1]
player = state[0]
if player == 0:
for i in range(6):
if lst[i] > 0:
actions.append(i)
return actions
else:
for i in range(6):
if lst[i+7] >0: actions.append(i+7)
return actions
# TODO: implement mancala_of(player)
# Return the numeric position of the given player's mancala.
# Player 0's mancala is on the right and player 1's mancala is on the left.
# You can assume player is either 0 or 1.
def mancala_of(player: int) -> int:
if player ==0: return 6
elif player==1: return 13
# TODO: implement pits_of(player)
# Return a list of numeric positions corresponding to the given player's pits.
# The positions in the list should be ordered from lowest to highest.
# Player 0's pits are on the bottom and player 1's pits are on the top.
# You can assume player is either 0 or 1.
def pits_of(player: int) -> list:
if player ==0:
return [0,1,2,3,4,5]
elif player==1:
return [7,8,9,10,11,12]
# TODO: implement player_who_can_do(move)
# Return the player (either 0 or 1) who is allowed to perform the given move.
# The move is allowed if it is the position of one of the player's pits.
# For example, position 2 is one of player 0's pits.
# So player_who_can_do(2) should return 0.
# You can assume that move is a valid position for one of the players.
def player_who_can_do(move: int) -> int:
if move in [0,1,2,3,4,5] : return 0
elif move in [7,8,9,10,11,12]: return 1
# TODO: implement opposite_from(position)
# Return the position of the pit that is opposite from the given position.
# Check the pdf instructions for the definition of "opposite".
def opposite_from(position: int) -> int:
d_p_1 = {}
d_p_1[0]=12
d_p_1[1]=11
d_p_1[2]=10
d_p_1[3]=9
d_p_1[4]=8
d_p_1[5]=7
d_p_1[7]=5
d_p_1[8]=4
d_p_1[9]=3
d_p_1[10]=2
d_p_1[11]=1
d_p_1[12]=0
return d_p_1[position]
# TODO: implement play_turn(move, board)
# Return the new game state after the given move is performed on the given board.
# The return value should be a tuple (new_player, new_board).
# new_player should be the player (0 or 1) whose turn it is after the move.
# new_board should be a list representing the new board state after the move.
#
# Parameters:
# board is a list representing the current state of the game board before the turn is taken.
# move is an int representing the position where the current player picks up gems.
# You can assume that move is a valid move for the current player who is taking their turn.
# Check the pdf instructions for the detailed rules of taking a turn.
#
# It may be helpful to use several of the functions you implemented above.
# You will also need control flow such as loops and if-statements.
# Lastly, the % (modulo) operator may be useful:
# (x % y) returns the remainder of x / y
# from: https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex
def play_turn(move: int, board: list) -> tuple:
player = player_who_can_do(move)
new_board = board
gems = new_board[move]
new_board[move] = 0
hasht = {}
hasht[0] =1
hasht[1] = 0
if player ==0:
x =0
offset = 1
gems_counter = gems
for i in range(gems):
if i + move + offset == 13: offset += 1
elif (i+move+offset) - 14 == 13: offset += 1
if i + move +offset > 13:
gem_position = (i+move+offset) - 14
else:
gem_position = i + move + offset
new_board[gem_position] += 1
gems_counter -= 1
if gems_counter ==0 and gem_position==6: x = 1
if gems_counter==0 and gem_position in pits_of(0) and new_board[gem_position] == 1 and new_board[opposite_from(gem_position)] > 0:
gems_from_myside = new_board[gem_position]
gems_from_opside = new_board[opposite_from(gem_position)]
new_board[6] = gems_from_myside+gems_from_opside
new_board[gem_position] = 0
new_board[opposite_from(gem_position)] = 0
return (hasht[x],new_board)
if player ==1:
x_2 = 1
offset = 1
gems_counter2 = gems
for i in range(gems):
if i + move + offset == 6: offset += 1
elif (i+move+offset) - 14 == 6: offset += 1
if i + move +offset > 13:
gem_position = (i+move+offset) - 14
else:
gem_position = i + move + offset
new_board[gem_position] += 1
gems_counter2 -= 1
if gems_counter2 == 0 and gem_position == 13: x_2 = 0
if gems_counter2==0 and gem_position in pits_of(1) and new_board[gem_position] == 1 and new_board[opposite_from(gem_position)] > 0:
gems_from_myside = new_board[gem_position]
gems_from_opside = new_board[opposite_from(gem_position)]
new_board[13] = gems_from_myside+gems_from_opside
new_board[gem_position] = 0
new_board[opposite_from(gem_position)] = 0
return (hasht[x_2],new_board)
# TODO: implement clear_pits(board)
# Return a new list representing the game state after clearing the pits from the board.
# When clearing pits, any gems in a player's pits get moved to that player's mancala.
# Check the pdf instructions for more detail about clearing pits.
def clear_pits(board: list) -> list:
length = len(board)
middle_index = length // 2
first_half = board[:middle_index]
second_half = board[middle_index:]
for i in range(6):
first_half[6] += first_half[i]
first_half[i]=0
second_half[6] += second_half[i]
second_half[i] = 0
return (first_half+second_half)
# This one is done for you.
# Plays a turn and clears pits if needed.
def perform_action(action, state):
player, board = state
new_player, new_board = play_turn(action, board)
if 0 in [len(valid_actions((0, new_board))), len(valid_actions((1, new_board)))]:
new_board = clear_pits(new_board)
return new_player, new_board
# TODO: implement score_in(state)
# state is a (player, board) tuple
# Return the score in the given state.
# The score is the number of gems in player 0's mancala, minus the number of gems in player 1's mancala.
def score_in(state: tuple) -> int:
lst = state[1]
return lst[6] - lst[13]
# TODO: implement is_tied(board)
# Return True if the game is tied in the given board state, False otherwise.
# A game is tied if both players have the same number of gems in their mancalas.
# You can assume all pits have already been cleared on the given board.
def is_tied(board: list) -> bool:
if board[mancala_of(0)] - board[mancala_of(1)] == 0: return True
else: return False
# TODO: implement winner_of(board)
# Return the winning player (either 0 or 1) in the given board state.
# The winner is the player with more gems in their mancala.
# You can assume it is not a tied game, and all pits have already been cleared.
def winner_of(board: list) -> int:
if board[mancala_of(0)] > board[mancala_of(1)]: return 0
elif board[mancala_of(0)] < board[mancala_of(1)]: return 1
# TODO: implement string_of(board)
def string_of(board: list) -> str:
new_board = pad_all(board)
return '\n {} {} {} {} {} {}\n {} {}\n {} {} {} {} {} {}\n'.format(new_board[12],new_board[11],new_board[10],new_board[9],new_board[8],new_board[7],new_board[13],new_board[6],new_board[0],new_board[1],new_board[2],new_board[3],new_board[4],new_board[5])
-----------------------MinMax AI-------------------------------------------------------------
from os import stat
import numpy as np
from mancala_helpers import *
# A simple evaluation function that simply uses the current score.
def simple_evaluate(state):
return score_in(state)
# TODO
# Implement a better evaluation function that outperforms the simple one.
def better_evaluate(state):
#lst = state[1]
#return score_in(state)/2
return None
# depth-limited minimax as covered in lecture
def minimax(state, max_depth, evaluate):
# returns chosen child state, utility
# base cases
if game_over(state): return None, score_in(state)
if max_depth == 0: return None, evaluate(state)
# recursive case
children = [perform_action(action, state) for action in valid_actions(state)]
results = [minimax(child, max_depth-1, evaluate) for child in children]
_, utilities = zip(*results)
player, board = state
if player == 0: action = np.argmax(utilities)
if player == 1: action = np.argmin(utilities)
return children[action], utilities[action]
# runs a competitive game between two AIs:
# better_evaluation (as player 0) vs simple_evaluation (as player 1)
def compete(max_depth, verbose=True):
state = initial_state()
while not game_over(state):
player, board = state
if verbose: print(string_of(board))
if verbose: print("--- %s's turn --->" % ["Better","Simple"][player])
state, _ = minimax(state, max_depth, [better_evaluate, simple_evaluate][player])
score = score_in(state)
player, board = state
if verbose:
print(string_of(board))
print("Final score: %d" % score)
return score
if __name__ == "__main__":
score = compete(max_depth=4, verbose=True)
----------------------------------------playing the game---------------------------
from os import stat
from mancala_helpers import *
from mancala_minimax import minimax, simple_evaluate
def get_user_action(state):
actions = list(map(str, valid_actions(state)))
player, board = state
prompt = "Player %d, choose an action (%s): " % (player, ",".join(actions))
while True:
action = input(prompt)
if action in actions: return int(action)
print("Invalid action, try again.")
if __name__ == "__main__":
max_depth = 1
state = initial_state()
while not game_over(state):
player, board = state
print(string_of(board))
if player == 0:
action = get_user_action(state)
state = perform_action(action, state)
else:
print("--- AI's turn --->")
#print(string_of(board))
print(state)
print(max_depth)
state, _ = minimax(state, max_depth, simple_evaluate)
#print(string_of(board))
player, board = state
print(string_of(board))
if is_tied(board):
print("Game over, it is tied.")
else:
winner = winner_of(board)
print("Game over, player %d wins." % winner)
the entire problem was in the mancala helper file. in the function play_turn() the 2nd line new_board = board. this was causing the issue because the original state should be immutable to work properly. any changes on new_board were also affecting board. the following new_board = copy.deepcopy(board) fixed everything. the function copy.deepcopy() creates a completely new copy, any changes applied to one of them does not affect the other.
I am trying to create the war card game where 2 players draw a card each on the table, and the player whose card has max value gets both the cards, if the card values are equal(called war condition) each player draws 5 cards, and the value of the last card among these 5 cards are compared and works similarly as above. A player wins when either the other player runs out of cards or if in war condition the other player has less than 5 cards.
My questions:
When I am running the game logic sometimes it runs but sometimes it gets stuck in an infinite loop. Why?
I can notice that when the total number of cards for both players at all times should be 52, but here it is decreasing. Why? (I can see that the cards are getting lost every time the game goes into war condition, but I am not able to understand why that is happening, since I am adding all the 10 cards to the player whose card has a greater value.)
I have tried another method where I assume that players are always at war and then approach it, which works. But I want to understand why, if I break it into steps which I am trying to do here, is it not working?
Code:
import random
suits = ['Hearts','Clubs','Spades','Diamonds']
ranks = ['Two','Three','Four','Five','Six','Seven','Eight','Nine','Ten','Jack','Queen','King','Ace']
values = {'Two': 2, 'Three': 3, 'Four': 4, 'Five': 5, 'Six' : 6 , 'Seven' :7, 'Eight': 8,'Nine':9,
'Ten':10,'Jack':11, 'Queen': 12,'King': 13,'Ace':14 }
classes:
class Card():
def __init__(self,suit,rank):
self.suit = suit
self.rank = rank
self.value = values[rank]
#string method
def __str__(self):
return self.rank + ' of ' + self.suit
class Deck():
def __init__(self):
self.all_cards = [] #list of objects
for suit in suits:
for rank in ranks:
#create card object
created_card = Card(suit,rank)
self.all_cards.append(created_card)
def shuffle(self): #method to shuffle the card
random.shuffle(self.all_cards)
def deal_one(self):
return self.all_cards.pop() #we want the last card from deck
class Player():
def __init__(self,name):
self.name = name
self.all_cards = []
def remove_one(self):
return self.all_cards.pop(0) #to remove card from beginning of the list
def add_cards(self,new_cards):
if type(new_cards) == type([]):
self.all_cards.extend(new_cards)
else:
self.all_cards.append(new_cards)
def __str__(self):
return f'Player {self.name} has {len(self.all_cards)} cards.'
And below is the final game logic:
#create 2 new instances of the player class
player1_name = input('Enter the name of Player1: ')
player2_name = input('Enter the name of Player2: ')
player1 = Player(player1_name)
player2 = Player(player2_name)
newdeck = Deck()
newdeck.shuffle()
#splitting the deck among the two players - alternate card from deck goes to each player respectively
for i in range(0,len(newdeck.all_cards)-1,2):
player1.add_cards(newdeck.all_cards[i])
player2.add_cards(newdeck.all_cards[i+1])
#for x in range(26):
# player1.add_cards(newdeck.deal_one())
#player2.add_cards(newdeck.deal_one())
print(player1)
print(player2)
game_status = True
round_num = 0
while game_status == True:
round_num +=1
print(f"Round {round_num}")
if len(player1.all_cards) == 0:
print('Player 1 out of cards'+ player2.name + 'Wins!')
game_status = False
break
if len(player2.all_cards) == 0:
print('Player 2 out of cards' + player1.name + 'Wins!')
game_status = False
break
else:
player_cards = []
player1_card = player1.remove_one()
player2_card = player2.remove_one()
player_cards.append(player1_card)
player_cards.append(player2_card)
print('player1_card_value: ',player1_card.value)
print('')
print('player2_card_value: ',player2_card.value)
print('')
print(player1)
print('')
print(player2)
at_war = True
if player1_card.value == player2_card.value:
while at_war == True:
player1_list = []
player2_list = []
card_list = []
if len(player1.all_cards) < 5:
print('Player 2 won')
game_status = False
at_war = False
break
elif len(player2.all_cards) <5:
print('Player 1 won')
game_status = False
at_war = False
break
if len(player1.all_cards) >= 5 and len(player2.all_cards) >= 5:
for i in range(5):
player1_list.append(player1.remove_one())
player2_list.append(player2.remove_one())
card_list.extend(player1_list)
card_list.extend(player2_list)
print("CARD LIST LEN", len(card_list))
if player1_list[0].value > player2_list[0].value:
player1.add_cards(card_list)
at_war = False
break
elif player1_list[0].value < player2_list[0].value:
player2.add_cards(card_list)
#player2.add_cards(player1_list)
at_war = False
break
else:
at_war = True
elif player1_card.value > player2_card.value:
player1.add_cards(player_cards)
#player1.add_cards(player2_cards)
#print('p1>p2', player1)
elif player2_card.value > player1_card.value:
player2.add_cards(player_cards)
print(player1)
print(player2)
if len(player1.all_cards) == 0:
print('Player 2 won')
elif len(player2.all_cards) == 0:
print('Player 1 won')
Output: When it was stuck in an infinite loop:(You can see the total number of cards is now 34 instead of 52.)
This question already has an answer here:
NoneType object is not subscriptable
(1 answer)
Closed 2 years ago.
I'm trying to write code for a text based RPG by following along in a book called Make Your Own Python Text Adventure by Phillip Johnson. I am having trouble identifying where the error lies in my code. I've gone through it several times and can't seem to figure it out. I've tried fiddling with different things but so far it just gives different errors. I'll post my code and the Traceback.
Traceback (most recent call last):
File "C:\Users\Timothy Hall\Desktop\Yradel_Game\Yradel_Game5.py", line 99, in <module>
main()
File "C:\Users\Timothy Hall\Desktop\Yradel_Game\Yradel_Game5.py", line 31, in main
player = Player()
File "C:\Users\Timothy Hall\Desktop\Yradel_Game\player.py", line 11, in __init__
self.x = world.start_tile_location[0]
TypeError: 'NoneType' object is not subscriptable
Yradel_Game5.py
# this program is a text based rpg
#import the OrderedDict
from collections import OrderedDict
# import the player module
from player import Player
# import the world module
import world
def main():
# display Yradellian name examples
print("Some examples of female Yradellian names include Nelsi, Drew, Casey, Ilya, etc.")
print("Some examples of male Yradellian names include Toreth, Daren, Attel, Mayes, etc.")
# get the character name from the user
yesName = False
while yesName == False:
charName = input("What can I call you? ")
nameInput = input("Your name is, " + charName + "? Type Yes or No: ")
if nameInput == "Yes":
yesName = True
else:
print("Sorry...")
# call the parse world function
world.parse_world_dsl()
# create a player object
player = Player()
# display the welcome message
print()
player.say_hello(charName)
# display current inventory
print()
print("Here's your starting game Inventory:")
player.print_inventory()
while True:
# display the intro text for each tile
print()
room = world.tile_at(player.x, player.y)
print(room.intro_text())
# modify the player depending on the tile type
room.modify_player(player)
# get the action input from the user
print()
choose_action(room, player)
# create a funtion for available actions
def get_available_actions(room, player):
actions = OrderedDict()
print("Choose what to do...")
if player.inventory:
action_adder(actions, "i", player.print_inventory, "Print inventory")
if isinstance(room, world.TraderTile):
action_adder(actions, "t", player.trade, "Trade")
if isinstance(room, world.EnemyTile) and room.enemy.is_alive():
action_adder(actions, "a", player.attack, "Attack")
else:
if world.tile_at(room.x, room.y - 1):
action_adder(actions, "n", player.move_north, "Go North!")
if world.tile_at(room.x + 1, room.y):
action_adder(actions, "e", player.move_east, "Go East!")
if world.tile_at(room.x, room.y + 1):
action_adder(actions, "s", player.move_south, "Go South!")
if world.tile_at(room.x - 1, room.y):
action_adder(actions, "w", player.move_west, "Go West!")
if player.hp < 100:
action_adder(actions, "h", player.heal, "Heal")
return actions
# create the action adder function
def action_adder(action_dict, hotkey, action, name):
action_dict[hotkey.lower()] = action
action_dict[hotkey.upper()] = action
print("{}: {}".format(hotkey, name))
# create a function to utilize the action dictionary
def choose_action(room, player):
action = None
while not action:
available_actions = get_available_actions(room, player)
action_input = input("Action: (Type a letter) ")
action = available_actions.get(action_input)
if action:
action()
else:
print("That is invalid input.")
# call the main function
main()
world.py
# import the enemies module
import enemies
# import the npc module
import npc
# import the random module
import random
# create a parent class for the map tiles
class MapTile:
def __init__(self, x, y):
self.x = x
self.y = y
def intro_text(self):
raise NotImplementedError("Create a subclass instead!")
def modify_player(self, player):
pass
# create the tile subclasses
class StartTile(MapTile):
def intro_text(self):
return """You find yourself in a forest, sunlight trickling through the leaves overhead. Your feet crunch over the underbrush. You can see four paths through the trees.\n"""
class BoringTile(MapTile):
def intro_text(self):
return """The trees all look the same here...\n"""
class CityTile(MapTile):
def intro_text(self):
return """You made it out of the forest into a small town known as Burenburg. The people greet you warmly and you are filled with a sense of accomplishment.\n"""
class EnemyTile(MapTile):
# have enemies randomly appear
def __init__(self, x, y):
r = random.random()
if r < 0.50:
self.enemy = enemies.Wolf()
self.alive_text = "A lone Wolf approaches you baring its fangs."
self.dead_text = "The Wolf keels over, dead before you."
else:
self.enemy = enemies.Goblin()
self.alive_text = "A Goblin tries to steal your gold, you must defend yourself against his blade."
self.dead_text = "The Goblin sticks its tongue out at you as it falls over dead."
super().__init__(x, y)
# display their alive/dead message
def intro_text(self):
text = self.alive_text if self.enemy.is_alive() else self.dead_text
return text
# have enemies attack the player
def modify_player(self, player):
if self.enemy.is_alive():
player.hp = player.hp - self.enemy.damage
print("Enemy does {} damage. You have {} HP remaining.".format(self.enemy.damage, player.hp))
class TraderTile(MapTile):
def __init__(self, x, y):
self.trader = npc.Trader()
super().__init__(x, y)
# create a method to trade between a buyer and seller
def trade(self, buyer, seller):
for i, item in enumerate(seller.inventory, 1):
print("{}. {} - {} Gold".format(i, item.name, item.value))
while True:
user_input = input("Choose an item or press Q to exit: ")
if user_input in ["Q", "q"]:
return
else:
try:
choice = int(user_input)
to_swap = seller.inventory[choice - 1]
self.swap(seller, buyer, to_swap)
except ValueError:
print("Invalid choice!")
# create a method to swap items and gold between the buyer and seller
def swap(self, seller, buyer, item):
if item.value > buyer.gold:
print("That's too expensive")
return
seller.inventory.remove(item)
buyer.inventory.append(item)
seller.gold = seller.gold + item.value
buyer.gold = buyer.gold - item.value
print("Trade complete!")
# create a method to accept user input about who is the buyer and seller
def check_if_trade(self, player):
while True:
print("Would you like to (B)uy, (S)ell or (Q)uit?")
user_input = input()
if user_input in ["Q", "q"]:
return
elif user_input in ["B", "b"]:
print("Here's what's available to buy: ")
self.trade(buyer = player, seller = self.trader)
elif user_input in ["S", "s"]:
print("Here's what's available to sell: ")
self.trade(buyer = self.trader, seller = player)
else:
print("Invalid choice!")
# create intro text for this room
def intro_text(self):
return """A man in a brown robe awaits you, willing to trade."""
class GoldTile(MapTile):
def __init__(self, x, y):
self.gold = random.randint(1, 50)
self.gold_claimed = False
super().__init__(x, y)
def modify_player(self, player):
if not self.gold_claimed:
self.gold_claimed = True
player.gold = player.gold + self.gold
print("+{} Gold added.".format(self.gold))
def intro_text(self):
if self.gold_claimed:
return """Another clearing in the forest with nothing in it but cute animals and underbrush."""
else:
return """Someone must've dropped some gold! You pick it up."""
# create the basic world map
world_dsl = """
| |E|C|E| |
|B|B| |B| |
|T| |G|E|B|
|G| |E| |G|
|E|B|S|T|B|
| | |E| |G|
| | |G|B|E|
"""
# create a function to validate the dsl
def is_dsl_valid(dsl):
if dsl.count("|S|") != 1:
return False
if dsl.count("|C|") == 0:
return False
lines = dsl.splitlines()
lines = [l for l in lines if l]
pipe_counts = [line.count("|") for line in lines]
for count in pipe_counts:
if count != pipe_counts[0]:
return False
return True
# define a dictionary that maps dsl abreviations to tile types
tile_type_dict = {"C": CityTile,
"E": EnemyTile,
"S": StartTile,
"B": BoringTile,
"G": GoldTile,
"T": TraderTile,
" ": None}
world_map = []
start_tile_location = None
# create the function to parse the dsl
def parse_world_dsl():
if not is_dsl_valid(world_dsl):
raise SyntaxError("DSL is invalid!")
dsl_lines = world_dsl.splitlines()
dsl_lines = [x for x in dsl_lines if x]
# iterate over each line in the dsl
# instead of i, the variable y is used because we're working with an X-Y grid.
for y, dsl_row in enumerate(dsl_lines):
# create an object to store the tiles
row = []
# split the line into abbreviations using the "split" method
dsl_cells = dsl_row.split("|")
# the split method includes the beginning and end of the line
# so we need to remove those nonexistent cells
dsl_cells = [ c for c in dsl_cells if c]
# iterate over each cell in the dsl line
# instead of j, the variable x is used becuase we're working with an X-Y grid
for x, dsl_cell in enumerate(dsl_cells):
# look up the abbreviation in the dictionary
tile_type = tile_type_dict[dsl_cell]
# if the dictionary returned a valid type, create a new
# tile object, pass it the X-Y coordinates as required
# by the tile__init__(), and add it to the row object. If
# None was found in the dictionary, we just add None.
row.append(tile_type(x, y) if tile_type else None)
# add the whole row to the world_map
world_map.append(row)
# create a function that locates the tile at a specific coordinate
def tile_at(x, y):
if x < 0 or y < 0:
return None
try:
return world_map[y][x]
except IndexError:
return None
player.py
# import the items module
import items
# import the world module
import world
# create a class for Player
class Player:
# define the inventory
def __init__(self):
self.inventory = [items.Dagger(), items.BreadRoll(), items.Potion()]
self.x = world.start_tile_location[0]
self.y = world.start_tile_location[1]
self.hp = 100
self.gold = 100
# print the inventory and the best weapon
def print_inventory(self):
print("Inventory:")
for item in self.inventory:
print("* " + str(item))
print("Gold: {}".format(self.gold))
best_weapon = self.most_powerful_weapon()
print("Your best weapon is your {}".format(best_weapon))
# create a function to heal the player
def heal(self):
consumables = [item for item in self.inventory if isinstance(item, items.Consumable)]
if not consumables:
print("You don't have any items to heal you!")
return
for i, item in enumerate(consumables, 1):
print("Choose an item to use to heal: ")
print("{}. {}".format(i, item))
valid = False
while not valid:
choice = input("")
try:
to_eat = consumables[int(choice) - 1]
self.hp = min(100, self.hp + to_eat.healing_value)
self.inventory.remove(to_eat)
print("Current HP: {}".format(self.hp))
valid = True
except (ValueError, IndexError):
print("Invalid choice, try again.")
# welcome the user to the game
def say_hello(self, name):
print("Hello, " + name, ", welcome to the world of Yradel.")
# create a function to determine the best weapon
def most_powerful_weapon(self):
max_damage = 0
best_weapon = None
for item in self.inventory:
try:
if item.damage > max_damage:
best_weapon = item
max_damage = item.damage
except AttributeError:
pass
return best_weapon
# create functions to let the player move
def move(self, dx, dy):
self.x += dx
self.y += dy
def move_north(self):
self.move(dx = 0, dy = -1)
def move_east(self):
self.move(dx = 1, dy = 0)
def move_south(self):
self.move(dx = 0, dy = 1)
def move_west(self):
self.move(dx = -1, dy = 0)
# create a function to attack the enemy
def attack(self):
best_weapon = self.most_powerful_weapon()
room = world.tile_at(self.x, self.y)
enemy = room.enemy
print("You use {} against the {}!".format(best_weapon.name, enemy.name))
enemy.hp -= best_weapon.damage
if not enemy.is_alive():
print("You killed {}!".format(enemy.name))
else:
print("{} HP is {}.".format(enemy.name, enemy.hp))
# create a method to allow trade
def trade(self):
room = world.tile_at(self.x, self.y)
room.check_if_trade(self)
It looks like you forgot to set start_tile_location in this function:
start_tile_location = None
# create the function to parse the dsl
def parse_world_dsl():
if not is_dsl_valid(world_dsl):
raise SyntaxError("DSL is invalid!")
dsl_lines = world_dsl.splitlines()
dsl_lines = [x for x in dsl_lines if x]
# iterate over each line in the dsl
# instead of i, the variable y is used because we're working with an X-Y grid.
for y, dsl_row in enumerate(dsl_lines):
# create an object to store the tiles
row = []
# split the line into abbreviations using the "split" method
dsl_cells = dsl_row.split("|")
# the split method includes the beginning and end of the line
# so we need to remove those nonexistent cells
dsl_cells = [ c for c in dsl_cells if c]
# iterate over each cell in the dsl line
# instead of j, the variable x is used becuase we're working with an X-Y grid
for x, dsl_cell in enumerate(dsl_cells):
# look up the abbreviation in the dictionary
tile_type = tile_type_dict[dsl_cell]
# if the dictionary returned a valid type, create a new
# tile object, pass it the X-Y coordinates as required
# by the tile__init__(), and add it to the row object. If
# None was found in the dictionary, we just add None.
row.append(tile_type(x, y) if tile_type else None)
if "S" == dsl_cell:
start_tile_location = (x, y)
# add the whole row to the world_map
world_map.append(row)
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!