python - 'NoneType' object has no attribute - python

I'm new with Python/programming and trying to solve little problems to get a hang of it.
I've been struggling with the error below and not too sure how i got it. I understand it is saying the file type is None which is why it's throwing the error. However, I don't get how it is None in the first place? Just wondering if I could get some guidance or hint on the issue? Thank you very much, sorry if the code is messy
line 138, in move
self.ecoList[self.temP].nP = tempAni.p AttributeError: 'NoneType' object has no attribute 'nP'
from random import randint
class Bears:
def __init__(self):
self.p = 0
self.dp = 0
self.nP = 0
self.check = True
def run(self):
self.dp = randint(-1, 1)
self.nP = self.dp + self.p
self.check = False
def __str__(self):
return "Bear_"
def __repr__(self):
return self.__str__()
class Fish:
def __init__(self):
self.p = 0
self.dp = 0
self.nP = 0
self.check = True
def run(self):
self.dp = randint(-1, 1)
self.nP = self.dp + self.p
self.check = False
def __str__(self):
return "Fish|"
def __repr__(self):
return self.__str__()
class EcoSystem:
def __init__(self):
self.ecoList = [None] * 10
self.bearList = []
self.fishList = []
for i in range(2):
self.bearList.append(Bears())
#Adding bear to ecoList
while True:
index = randint(0, 9)
if self.ecoList[index] is None:
self.ecoList[index] = self.bearList[i]
self.ecoList[index].p = index
break
else:
continue
for i in range(2):
self.fishList.append(Fish())
#Adding fish to ecoList
while True:
index = randint(0, 9)
if self.ecoList[index] is None:
self.ecoList[index] = self.fishList[i]
self.ecoList[index].p = index
break
else:
continue
self.move()
def move(self):
#Print out the current eco system
print(*self.ecoList, sep='\n')
anwser = True
while anwser:
#populate next move new position for each object
for i in range(len(self.ecoList)):
if self.ecoList[i] is None:
continue
else:
self.ecoList[i].run()
#run for loop to test next position of the object
for i in range (len(self.ecoList)):
#if [i] item is None skip to next loop
if self.ecoList[i] is None:
continue
elif self.ecoList[i].check == True:
continue
#else check if it is going to move then check adjacent slot is going to be taken
else:
tempAni = None #temp animal to compare with item in loop
#call out new position from item i
newP = self.ecoList[i].nP
#call out direction:
newDP = self.ecoList[i].dp
#do nothing, skip to next slot if it is not going to move
if newDP == 0:
self.ecoList[i].check = True
continue
elif newDP != 0:#test if new position is going to be out of bound
if newP < 0 or newP > (len(self.ecoList)-1):
#set new position back to current
self.ecoList[i].nP = self.ecoList[i].p
self.ecoList[i].dp = 0
self.ecoList[i].check = True
else:
#test if new position is going to be collided
if self.ecoList[newP] is not None:
if self.ecoList[newP].nP == self.ecoList[i].nP:
print("////////////////")
tempAni = self.ecoList[newP]
#test if the next next new position is not None or out of bound
#Assumption - prioritize the closet animal going to move
elif (newP+newDP) > 0 and (newP+newDP) < (len(self.ecoList)-1):
if self.ecoList[newP+newDP] is not None:
#test if this is going to be collided
if self.ecoList[newP+newDP].nP == self.ecoList[i].nP:
print("\\\\\\\\\\\\\\")
tempAni = self.ecoList[newP+newDP]
#if tempAni is not none compare the type
if tempAni is not None:
print ("####")
print (self.ecoList[i].p)
print (self.ecoList[i])
print("-----------")
print (tempAni.p)
print(tempAni)
print ("####")
#test if they are the same type
self.temP = tempAni.p
if tempAni.__class__.__name__ == self.ecoList[i].__class__.__name__:
#if they are, change new position to current position
self.ecoList[i].nP = self.ecoList[i].p
self.ecoList[i].check = True
print("?????")
print(self.temP)
print(tempAni)
print(tempAni.dp)
print(self.ecoList[i])
print(self.ecoList[i].dp)
self.ecoList[self.temP].nP = tempAni.p
self.ecoList[self.temP].check = True
#create new animal of the same type and put it to a random place on the list
#Assumption - if the list is full add do nothing
#Determine tempAni type to create new bear or fish
if isinstance(tempAni, Bears):
#create new bear
newAni = Bears()
else:
#creaete new fish
newAni = Fish()
#while loop if the list is still have available spot add new animal to random spot, otherwise do nothing
while None in self.ecoList:
index = randint(0, 9)
if self.ecoList[index] is None:
self.ecoList.insert(index, newAni)
self.ecoList[index].p = index
print ("*****")
print (self.ecoList[index].p)
print (self.ecoList[index])
print ("*****")
break
#if they are not the same type, kill the fish
else:
#determine if tempAni is the fish or bear
if isinstance(tempAni, Bears):
#if it is bears kill the fish in i
self.ecoList[i].p = -1
self.ecoList[i].check = True
self.ecoList[self.temP].check = True
elif isinstance(tempAni, Fish):
#if it is fish kill it
self.ecoList[self.temP].p = -1
self.ecoList[i].check = True
self.ecoList[self.temP].check = True
#Apply the change after all the checks are finished
#Remove all the fish got killed and apply the moves
for i in range (len(self.ecoList)):
if self.ecoList[i] is not None:
if self.ecoList[i].p == -1:
self.ecoList[i] = None
elif self.ecoList[i].check == False:
self.ecoList[i].check = True
newP = self.ecoList[i].nP
if newP != i:
self.ecoList[newP] = self.ecoList[i]
self.ecoList[newP].p = newP
self.ecoList[i] = None
#Print out the current eco system
print ("---------------------------------------")
for i in range (len(self.ecoList)):
print(self.ecoList[i])
print(i)
#Ask if user want to continue playing
test = True
while test == True:
strAns = input ('Enter y/n to continue or not: ')
if strAns.lower() == "n":
anwser = False
test = False
break
elif strAns.lower() == "y":
test = False
break
def main():
EcoSystem()
main()

The error means that self.ecoList[self.temP] is None, although your code does not expect it to be None. So, this is the offending line:
self.ecoList[self.temP].nP = tempAni.p
Your code actually wants to assign tempAni.p to None.nP. None does not have such an attribute, and that is why you get the error. The line where the code throws the exception is just an indication that something is wrong in your code, somewhere. Finding this bug is your task now.
You need to breathe calmly and step-by-step figure out where your code is wrong. This might be anywhere and nobody here on SO will find this for you. Add print and/or assert statements to your code, and narrow down the issue. This is debugging work, and you have to go through it!

Related

Created an endless loop, but can't tell why it is endless

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.

Implementing an aima-python agent

I am using aima-python to implement a covid cleaning agent, this agent will go to each square and spray. If there is a an object present in the cell it will still spray such as table, chair etc. However, if there is a person in the next cell the robot cannot cross paths with the cell and must avoid entering a cell with a human. I have implemented most of the code and I think the code does as described above, I am however getting a 'TypeError: 'chair' object is not callable'. I can't see why I would be getting this, this is the same for all object present in my environment. I would really appreciate some assistance with this issue.(This program runs on jupyter notebook)
from agents import *
from random import choice
class DisinfectingRobot(Agent):
location = [0,1]
direction = Direction("down")
def moveforward(self, success=True):
'''moveforward possible only if success (i.e. valid destination location)'''
if not success:
return
if self.direction.direction == Direction.R:
self.location[0] += 1
elif self.direction.direction == Direction.L:
self.location[0] -= 1
elif self.direction.direction == Direction.D:
self.location[1] += 1
elif self.direction.direction == Direction.U:
self.location[1] -= 1
def turn(self, d):
if isinstance(thing,person):
self.direction = self.direction + d
return True
return False
def spray(self, thing):
'''returns True upon success or False otherwise'''
if isinstance(thing, Food):
return True
return False
def program(percepts):
'''Returns an action based on it's percepts'''
for p in percepts:
if isinstance(p, chair):
return 'spray'
elif isinstance(p, table):
return 'spray'
elif isinstance(p,person):
turn = False
choice = random.choice((1,2))
else:
choice = random.choice((1,2,3,4))
if isinstance(p,Bump): # then check if you are at an edge and have to turn
turn = False
choice = random.choice((1,2));
else:
choice = random.choice((1,2,3,4)) # 1-right, 2-left, others-forward
if choice == 1:
return 'turnright'
elif choice == 2:
return 'turnleft'
else:
return 'moveforward'
class chair(Thing):
pass
class table(Thing):
pass
class person(Thing):
pass
class lab2D(GraphicEnvironment):
def percept(self, agent):
'''return a list of things that are in our agent's location'''
things = self.list_things_at(agent.location)
loc = copy.deepcopy(agent.location) # find out the target location
#Check if agent is about to bump into a wall
if agent.direction.direction == Direction.R:
loc[0] += 1
elif agent.direction.direction == Direction.L:
loc[0] -= 1
elif agent.direction.direction == Direction.D:
loc[1] += 1
elif agent.direction.direction == Direction.U:
loc[1] -= 1
if not self.is_inbounds(loc):
things.append(Bump())
return things
def execute_action(self, agent, action):
'''changes the state of the environment based on what the agent does.'''
if action == 'turnright':
print('{} decided to {} at location: {}'.format(str(agent)[1:-1], action, agent.location))
agent.turn(Direction.R)
elif action == 'turnleft':
print('{} decided to {} at location: {}'.format(str(agent)[1:-1], action, agent.location))
agent.turn(Direction.L)
elif action == 'moveforward':
print('{} decided to move {}wards at location: {}'.format(str(agent)[1:-1], agent.direction.direction, agent.location))
agent.moveforward()
elif action == "spray":
items = self.list_things_at(agent.location, tclass=chair)
if len(items) != 0:
if agent.spray(items[0]):
print('{} sprayed {} at location: {}'
.format(str(agent)[1:-1], str(items[0])[1:-1], agent.location))
elif action == "turn":
items = self.list_things_at(agent.location, tclass=person)
if len(items) != 0:
if agent.turn(items[0]):
print('{} turned because {} at location: {}'
.format(str(agent)[1:-1], str(items[0])[1:-1], agent.location))
lab = lab2D(5,5, color={'DisinfectingRobot': (128,128,128), 'chair': (153, 76, 0),
'table': (230, 115, 40), 'person': (51,25,0)})
robot = DisinfectingRobot(program)
lab.add_thing(robot, [0,0])
chair = chair()
table = table()
person = person()
lab.add_thing(robot, [0,0])
lab.add_thing(table, [1,2])
lab.add_thing(chair, [0,1])
lab.add_thing(person,[5,1])
moretable = table()
morechair = chair()
moreperson = person
lab.add_thing(moretable, [2,4])
lab.add_thing(morechair, [4,3])
lab.add_thing(moreperson,[3,3])
print("robot started at [0,0], facing down. Time to disinfect!")
lab.run(100)

UnboundLocalError: local variable 'validSqaures' referenced before assignment

I'm trying to follow a python tutorial on chess, but when i run the function to check for Checkmates and possible moves, I get the following error:
File "d:\Coding\Projects\Chess\stuff\ChessEngine.py", line 86, in getValidMoves
if not (moves[i].endRow, moves[i].endCol) in validSqaures:
UnboundLocalError: local variable 'validSqaures' referenced before assignment
This is the code I used for the function
def getValidMoves(self):
moves = []
self.inCheck, self.pins, self.checks = self.checkForPinsAndChecks()
if self.whiteToMove:
kingRow = self.whiteKingLocation[0]
kingCol = self.whiteKingLocation[1]
else:
kingRow = self.blackKingLocation[0]
kingCol = self.blackKingLocation[1]
if self.inCheck:
if len(self.checks) == 1: #Only 1 piece checking the King
moves = self.getAllPossibleMoves()
check = self.checks[0]
checkRow = check[0]
checkCol = check[1]
pieceChecking = self.board[checkRow][checkCol]
validSquares = [] #Sqaures pieces can move to
if pieceChecking[1] == "N":
validSqaures = [(checkRow, checkCol)]
else:
for i in range(1, 8):
validSquare = (kingRow + check[2] * i, kingCol + check[3] * i)
validSquares.append(validSquare)
if validSquares[0] == checkRow and validSquares[1] == checkCol:
break
for i in range(len(moves) -1 ,-1, -1):
if moves[i].pieceMoved[1] != 'K':
if len(moves) != 0:
if not (moves[i].endRow, moves[i].endCol) in validSqaures:
moves.remove(moves[i])
else: #Double check, King has to move
self.getKingMoves(kingRow, kingCol, moves)
else: #Not in check so any move is fine
moves = self.getAllPossibleMoves()
if len(moves) == 0: #either CheckMate or Stalemate
if self.inCheck == True:
self.checkMate = True
else:
self.staleMate = True
else:
self.checkMate = False
self.staleMate = False
return moves
When I try placing the list validSqaures anywhere else, it just turns blank and doesnt allow me to make anymore moves after putting the opponent king in check.

Micro:bit classes instead of global variables - memory allocation error in micropython

I've just created my first little truth or dare/spin the bottle game for a senior/high school coding club on the Micro:bit. I would like to introduce using oop/classes/objects instead of (dreaded) global vars. The game works great on emulators such as https://create.withcode.uk/, but on the Micro:bit itself I encounter memory allocation errors as soon as I try to put pretty much anything into classes. Is the microbit's 16KB of RAM not enough? Or am I declaring classes incorrectly?
Coming from front-end and a bit of PHP/SQL, I'm a Python/memory knowledge noob so any help is appreciated.
If I use global vars in each of the functions it works without issue.
Here is the code:
from microbit import *
import random
#timer current function
#Global variables
class game:
gsTime = 3000
timerPrev = 0
numOfPlayers = 0
maxPlayers = 8
stage = 'start'
minSpinTime = 3000
class player:
#Which player is currently selected
selected = 0
#Player position display
def pos1(): display.set_pixel(2, 4, 9)
def pos2(): display.set_pixel(2, 0, 9)
def pos3(): display.set_pixel(0, 2, 9)
def pos4(): display.set_pixel(4, 2, 9)
def pos5(): display.set_pixel(0, 4, 9)
def pos6(): display.set_pixel(4, 0, 9)
def pos7(): display.set_pixel(0, 0, 9)
def pos8(): display.set_pixel(4, 4, 9)
#Array of all player positions
positions = [
[pos1, 1, Image.ARROW_S],
[pos2, 5, Image.ARROW_N],
[pos3, 3, Image.ARROW_W],
[pos4, 7, Image.ARROW_E],
[pos5, 2, Image.ARROW_SW],
[pos6, 6, Image.ARROW_NE],
[pos7, 4, Image.ARROW_NW],
[pos8, 8, Image.ARROW_SE]
]
positionsOrdered = []
class buttons:
pressed = False
class spinner:
completeSpins = 0
isCompleteSpin = False
rdTime = 0
stage = 'start'
stageStarted = False
gameCall = game()
playerCall = player()
buttonsCall = buttons()
spinnerCall = spinner()
#Return a random range of numbers
def rdRange(minMult,maxMult,base):
return random.randint(base*minMult, base*maxMult)
#return sort key of list
def getKey(item):
return item[1]
#Timer function
def timer(timeElapsed, onCompleteFunc):
if running_time() - gameCall.timerPrev >= timeElapsed:
onCompleteFunc()
#Position Players Start is true
def positionPlayersStartTrue():
game.stage = 'positionPlayers'
def selectNumOfPlayers(gteOrLte):
game.timerPrev = running_time()
if gteOrLte == 'gte':
if gameCall.numOfPlayers >= gameCall.maxPlayers:
game.numOfPlayers = 1
else:
game.numOfPlayers += 1
else:
if gameCall.numOfPlayers <= 1:
game.numOfPlayers = maxPlayers
else:
game.numOfPlayers -= 1
display.show(str(gameCall.numOfPlayers)) #Have to convert int to string before passing to display.show func
buttons.pressed = True
#Ask for number of players up to maxPlayers.
def setPlayerNum():
#If B is pressed increment by 1 up the max players and cycle back to 1
if button_b.was_pressed():
selectNumOfPlayers('gte')
#If A is pressed decrement by 1 down to 1 and then cycle back to maxPlayers var
elif button_a.was_pressed():
selectNumOfPlayers('lte')
elif buttonsCall.pressed == False:
#Ask how many players
display.show('#?')
else:
timer(gameCall.gsTime, positionPlayersStartTrue)
#display the position of players
def positionPlayers():
buttons.pressed = False
display.clear()
for i in range(gameCall.numOfPlayers):
el = player.positions[i]
player.positionsOrdered.append(el)
el[0]()
player.positionsOrdered.sort(key=getKey)
while buttonsCall.pressed == False:
startSpin()
#start the spin - useful for starting the spin after one spin was complete too
def startSpin():
if button_a.was_pressed() or button_b.was_pressed():
buttons.pressed = True
game.stage = 'spin'
#Spin start
def spin():
if spinnerCall.stage == 'start' and spinnerCall.stageStarted == False:
game.timerPrev = running_time()
spinner.rdTime = rdRange(200, 700, gameCall.numOfPlayers)
spinner.stageStarted = True
print(spinner.rdTime)
for i in range(gameCall.numOfPlayers):
display.clear()
el = player.positionsOrdered[i]
el[0]()
if i + 1 == gameCall.numOfPlayers:
spinner.completeSpins += 1
spinner.isCompleteSpin = True
else:
spinner.isCompleteSpin = False
if spinnerCall.stage == 'start':
if (running_time() - gameCall.timerPrev >= (gameCall.minSpinTime + spinnerCall.rdTime)) and (spinnerCall.isCompleteSpin == True):
spinner.stage = 'slow'
spinner.stageStarted = False
sleep(200)
#Slower spin to emulate spinner slowing down as it comes near to stopping. Should probably use some clever-er maths here instead.
elif spinner.stage == 'slow':
if spinnerCall.stageStarted == False:
game.timerPrev = running_time()
spinner.rdTime = rdRange(500, 900, gameCall.numOfPlayers)
spinner.stageStarted = True
print(spinnerCall.rdTime)
if running_time() - gameCall.timerPrev >= spinnerCall.rdTime:
spinner.stage = 'stop'
spinner.stageStarted = False
sleep(400)
elif spinner.stage == 'stop':
player.selected = i
game.stage = 'selectedPlayer'
# reset spinner stage for next spin
spinner.stage = 'start'
break
#Player has been selected
def selectedPlayer():
el = playerCall.positionsOrdered[playerCall.selected]
sleep(200)
display.show(el[2])
sleep(200)
display.clear()
while True:
#CALL FUNCTIONS
if gameCall.stage == 'start':
setPlayerNum()
elif gameCall.stage == 'positionPlayers' and buttonsCall.pressed == True:
positionPlayers()
elif gameCall.stage == 'spin':
spin()
elif gameCall.stage == 'selectedPlayer':
#print('this one is selected ', playerCall.selected)
selectedPlayer()
#start spin again if button is pressed
startSpin()
Your code is too big for microbit. Microbit is limited by 16KB of RAM. To decrease size of your code you can:
minify it directly from Mu editor or use any other minifier lib
Shrink variable names
Delete comments

TicTacToe Python with board size selection

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!

Categories