Chess final: king and rook vs king in python - python

I've got some issues with this simple chess code in python. It is part of a weekly assignment; this is what I got so far:
from math import sqrt
from random import randint,shuffle,seed
def rand_pos():
return [randint(0,7),randint(0,7)]
#first, classes defining the kings and the rook;
#their only attribute is a randomly generated position on the chessboard.
#Each of them has its special print method that will be used in the Chessboard (Scacchiera) class.
class W_king:
def __init__(self,coord=rand_pos()):
self.coord=coord
self.x=coord[1]
self.y=coord[0]
def __repr__(self):
return "R"
class B_king:
def __init__(self,coord=rand_pos()):
self.coord=coord
self.x=coord[1]
self.y=coord[0]
def __repr__(self):
return "r"
class Rook:
def __init__(self,coord=rand_pos()):
self.coord=coord
self.x=coord[1]
self.y=coord[0]
def __repr__(self):
return "T"
#the following will be used to calculate the distance between the kings and between a king and the rook;
#I'll use it in the while statements later in the Scacchiera class to check if the kings are generated too near or stuff
def distance(n1,n2):
return sqrt(sum((n1.coord[i]-n2.coord[i])**2 for i in [0,1]))
class Scacchiera:
def __init__(self,w_king=W_king(),b_king=B_king(),rook=Rook(),boxes=[[" " for y in range(8)] for x in range(8)]):
self.w_king=w_king
self.b_king=b_king
self.rook=rook
self.boxes=boxes
#here it is: while the two kings are generated too near,
#get the black king new coordinates
while distance(self.b_king,self.w_king)<2:
self.b_king.coord=[randint(0,7),randint(0,7)]
#...and, while the white king (or the black king) and the rook have the same coordinates
#or the black king is in the rook's sight,
#get a new pair of coordinates for the rook:
while self.w_king.coord==self.rook.coord or self.b_king.coord==self.rook.coord or self.rook.x==self.b_king.x or self.rook.y==self.b_king.y:
self.rook.coord=[randint(0,7),randint(0,7)]
print distance(self.b_king,self.w_king) #to check, just for me
#the function conv switches to the chessboard's coordinates e.g. e4, h5, etc
print conv(self.w_king.coord),conv(self.b_king.coord),conv(self.rook.coord)
def __repr__(self):
#self.boxes is an array of blank spaces " ",
#and in the right place the kings and the rook are placed
scacchiera=self.boxes[:]
scacchiera[self.w_king.x][self.w_king.y]=self.w_king
scacchiera[self.b_king.x][self.b_king.y]=self.b_king
scacchiera[self.rook.x][self.rook.y]=self.rook
return "\n".join([str(8-i)+" "+" ".join(str(scacchiera[i][j]) for j in range(8)) for i in range(8)])+"\n "+" ".join([chr(97+k) for k in range(8)])
def check(self,king):
#no need for this for now
return self.rook.x==king.x or self.rook.y==king.y
def black_legal_moves(self,mossa):
future_king=B_king([self.b_king.y+mossa[0],self.b_king.x+mossa[1]])
if distance(self.w_king,future_king)<2 or self.check(future_king):
return False
else:
return True
def new_mossa_random(self):
#this method chooses randomly a new position for the black king from the list of adjacent cells
#and tests if it's legal with the method above. If it's not, it deletes it from the list and re-tries
moves_list=[[self.b_king.y+hor,self.b_king.x+ver] for ver in [-1,0,1] for hor in [-1,0,1] if not hor==ver==0]
shuffle(moves_list)
move=moves_list[0]
#while it's not legal or the coordinates are out of the board:
while not self.black_legal_moves(move) or not 0<=move[0]<=7 or not 0<=move[1]<=7:
del moves_list[0]
if not moves_list:
return None
move=moves_list[0]
return move
def conv(coord):
return [chr(coord[0]+97),8-coord[1]]
#you just need to run it:
seed()
scacchiera=Scacchiera()
print scacchiera
print conv(scacchiera.new_mossa_random())
The issues are two:
My code, though incomplete, seems correct to me in the chessboard generation section. Nonetheless, often (nearly three times out of ten) the kings are next to each other or the rook and a king are placed one over the other, or the random move for the black king isn't even near of his box.
Very often, the code keeps running and won't print any chessboard; it seems like it sticks on the two whiles at the beginning of Scacchiera.
NB: F5-ing the script on your PC will print, in the following order:
The distance between the two kings,
The coordinates on the chessboard of: the white king, the black king, and then the rook
The chessboard with the pieces on the board
The coordinates of a new random move for the black king.
Let me know if I should add some other info.

In case of collision, you're changing the coord member on the pieces. But the positions are also stored in x and y, which are not updated.
I'd suggest that you keep only x and y or only coord in your classes. if you want to get fancy, you could keep coord and make x and y into properties by using #property.

Your problems most likely stem from an incorrect use of default parameters.
Short answer, do this:
def __init__(self, coord=None):
coord = coord or rand_pos()
Explanation: Common gotcha with Python default args

Thank you for your help! Finally I removed the classes for the kings and the rook; they were pointless, all I needed was just a dictionary, just like in the code here.
Here is the solution I went through
from random import randint,shuffle,choice
def rand_pos():
return [randint(0,7),randint(0,7)]
def dist(n1,n2):
return (sum([(n1[i]-n2[i])**2 for i in [0,1]]))**(0.5)
def invconv(coord):
return [ord(coord[0])-97,8-coord[1]]
#the core of the game is here; Scacchiera means Checkboard, and it basically generates coordinates
#for the kings and the rook while they are in illegal positions.
#then there's a bunch of methods to determine wether a move is legal or not
#and, just for black, a random move is chosen.
#finally, all the stuff is gathered in the Partita (=Game) function.
class Scacchiera:
def __init__(self,w_king=rand_pos(),b_king=rand_pos(),rook=rand_pos()):
self.w_king=w_king
self.b_king=b_king
self.rook=rook
while dist(self.b_king,self.w_king)<=1.5:
self.b_king=rand_pos()
while self.w_king==self.rook or self.b_king==self.rook or self.rook[0]==self.b_king[0] or self.rook[1]==self.b_king[1]:
self.rook=rand_pos()
self.pezzi={"R":self.w_king,"r":self.b_king,"T":self.rook}
self.mosse=[self.pezzi[item] for item in ["r","R","T"]]
def __repr__(self):
griglia=[["." for j in range(8)] for i in range(8)]
for item in self.pezzi:
griglia[self.pezzi[item][0]][self.pezzi[item][1]]=item
return "\n".join([str(8-j)+" "+" ".join(str(griglia[i][j]) for i in range(8)) for j in range(8)])+"\n "+" ".join([chr(97+k) for k in range(8)])
def move(self,pezzo,end):
if not end:
return
end=[end[0]-self.pezzi[pezzo][0],end[1]-self.pezzi[pezzo][1]]
self.pezzi[pezzo][0]+=end[0]
self.pezzi[pezzo][1]+=end[1]
if self.pezzi["r"]==self.pezzi["T"]:
del self.pezzi["T"]
self.mosse.append(self.pezzi.values())
return
def check(self):
return self.pezzi["T"][0]==self.pezzi["r"][0] or self.pezzi["T"][1]==self.pezzi["r"][1]
def black_legal_move(self,end):
kings_dist=dist(self.pezzi["R"],end)
rook_king_dist=dist(self.pezzi["T"],self.pezzi["R"])
if kings_dist<=1.5:
return False
elif self.pezzi["T"]==end and rook_king_dist>1.5:
return True
elif self.pezzi["T"][0]==end[0] or self.pezzi["T"][1]==end[1] or end[0] not in range(8) or end[1] not in range(8):
return False
return True
def mosse_legali_b(self):
moves_list=[[self.pezzi["r"][0]+hor,self.pezzi["r"][1]+ver] for ver in [-1,0,1] for hor in [-1,0,1] if not hor==ver==0]
shuffle(moves_list)
elle=[]
for i in range(len(moves_list)):
if self.black_legal_move(moves_list[i]):
elle.append(moves_list[i])
if not elle:
return None
return elle
def mossa_random_black(self):
print "Tocca al nero.\n"
end=choice(self.mosse_legali_b())
if not end:
return None
self.move("r",end)
print self
return
def scacco_matto(self):
return self.check() and self.mosse_legali_b()==None
def stallo(self):
return self.mosse_legali_b()==None
def ripetizione(self):
return 3 in [self.mosse.count(item) for item in self.mosse]
def white_king_lmove(self,beg,end):
return dist(beg,end)<=1.5 and dist(self.pezzi["r"],end)>1.5 and beg!=end
def white_rook_lmove(self,beg,end):
return (beg[0]==end[0] or beg[1]==end[1]) and beg!=end
def white_legal_move(self,beg,end):
if self.pezzi["R"]==beg:
return self.white_king_lmove(beg,end)
else:
return self.white_rook_lmove(beg,end)
def mossa_white(self):
print "\n**Tocca al bianco**"
mossa=raw_input("Inserisci la prossima mossa:\n")
beg=invconv([mossa[0],int(mossa[1])])
end=invconv([mossa[2],int(mossa[3])])
if not self.white_legal_move(beg,end):
print "\nMossa non valida."
return self.mossa_white()
if self.pezzi["R"]==beg:
pezzo="R"
elif self.pezzi["T"]==beg:
pezzo="T"
self.move(pezzo,end)
if self.check():
print "Scacco!\n",self
else:
print self
return

Related

Problem setting the best move in the Negamax algorithm

Hey i am tring to make a chess engine but when i run the code it plays fine, the problem is sometimes it does not set the best move and it shows an error becuase the search did not return a move to play. My negamax includes AB pruning, MD Pruning, QSearch, ID, and TT Tables. Here is my implementation.
Entry=namedtuple('Entry', 'score depth flag')
class Search():
def __init__(self):
self.nodes=0
self.tt_score={}
def search(self,position,api):
self.nodes=0
for depth in range(1,1000):
ret=self.negamax(position,api,-INFINITY,INFINITY,depth,ply=1)
yield ret
def negamax(self,position,api,alpha,beta,depth=3,ply=1):
best,bmove=-INFINITY,()
for move in position.moves():
score=self.negasearch(position.move(move),-beta,-alpha,depth-1,ply+1)
if score>=beta: return [move,score,self.nodes]
if score>best:
best,bmove=score,move
#api.arrow("clear")
#api.arrow(coordinates[move[0]]+coordinates[move[1]])
if score>alpha: alpha=score
return [bmove,best,self.nodes]
def negasearch(self,position,alpha,beta,depth,ply):
best,aorig=-INFINITY,alpha
self.nodes+=1
if MATE-ply<beta:
beta=MATE-ply
if alpha>=MATE-ply: return MATE-ply
if -MATE+ply>alpha:
alpha=-MATE+ply
if beta<=-MATE+ply: return -MATE+ply
entry=self.tt_score.get(position.hash(),Entry(0,-1,'exact'))
if entry.depth>=depth:
if entry.flag=='exact': return entry.score
elif entry.flag=='lower': alpha=max(alpha,entry.score)
elif entry.flag=='upper': beta=min(beta,entry.score)
if alpha>=beta: return entry.score
if depth<=0: return self.qsearch(position,alpha,beta,ply+1)
for move in position.moves():
score=self.negasearch(position.move(move),-beta,-alpha,depth-1,ply+1)
if score>=beta:
best=score
break
if score>best:
best=score
if score>alpha: alpha=score
if best<=aorig: self.tt_score[position.hash()]=Entry(best,depth,'upper')
elif best>=beta: self.tt_score[position.hash()]=Entry(best,depth,'lower')
else: self.tt_score[position.hash()]=Entry(best,depth,'exact')
return best
def qsearch(self,position,alpha,beta,ply):
stand_pat=position.score
if stand_pat>=beta: return beta
if alpha<stand_pat: alpha=stand_pat
self.nodes+=1
for move in position.moves():
if move[2]==0: continue
score=self.qsearch(position.move(move),-beta,-alpha,ply+1)
if score>=beta: return beta
if score>alpha: alpha=score
return alpha
You probably have already noticed i did not negate the score each turn as it should be. Reason why is because when you call the move function it return the position with the score already negated. For example,
def move(self,move):
# copy the current position board, turn, and score
# if move is a capture then preform:
# score+=pvs[pieceBeingCaptured]
# preform move
# if the next player is mated after this move then set score to MATE
return Position(board,-score,self.turn*-1)
It is not the move generation because it returns the correct moves each position state. With the best value being -INFINITY and negamax it should always set the best move. Any help would be helpful.

chesspiece in list to announce winner

def destroy_piece(self, piece):
""" Removes piece from the canvans and click-handler
automatily called by move_piece
"""
img1=piece.get_img_int(0)
img2=piece.get_img_int(1)
del self._on_clicks[str(img1)]
del self._on_clicks[str(img2)]
self.delete(piece.get_img_int(0))
self.delete(piece.get_img_int(1))
self.destroyed_pieces = []
self.destroyed_pieces.append(piece)
for elem in self.destroyed_pieces:
if ......
messagebox.showinfo("WINNER")
class GUIKing(GUIChessPiece,King):
def __init__(self,board,row,col,color,path="./imagepack/"):
if color==BLACK:
path1=path+"bk.png"
path2=path+"bk_s.png"
else:
path1=path+"wk.png"
path2=path+"wk_s.png"
GUIChessPiece.__init__(self,board,row,col,color,path1,path2)
def on_click(self,event):
GUIChessPiece.on_click(self,event)
I am having trouble with continuing this code to detect win, simply by checking if the king is in the list, pieces are classes represented by pictures on a canvas. Though is it the images or the piece that is getting appended to the list? I am attaching the code for the king class
Because 'King' is a class, then to identify whether a member of that class is in the list self.destroyed_pieces you can do:
for elem in self.destroyed_pieces:
if isintance(elem,King):
messagebox.showinfo("WINNER")

How do I make my (derived?) python instance attributes "self-update" based on alterations to other same-instance attributes?

I know that I have a misunderstanding of how Python attributes work because I'm here writing this problem, but I don't know exactly what I'm misunderstanding. I'm trying to get
self.card = self.hand[self.card_number].split()
self.card_val = deck.ranks.get(self.card[0])
to attain their values based on self.hand, which I pass to __init__ upon instantiation. Throughout the game I am altering .hand, and I want .card & .card_val to change every time I change .hand, instead of having to tell it to do that elsewhere (outside of the attribute definitions). I know there is a way to do this, or at least I think there is, and by that I mean simply by defining their values as inherited based on whatever .hand is at any given time, without calling an internal or external function.
In the posted code, I have altered it to work as the game instructions require, using...
def get_card_vals(p1, p2):
for player in [p1, p2]:
player.card = player.hand[player.card_number].split()
player.card_val = deck.ranks.get(player.card[0])
print("{a} vs. {b}".format(a = p1.card, b = p2.card))
print("---------------------------")
...but that's what I want to change. I want what that function is doing to be executed more concisely inside of the attribute definitions upon handling of the instance. Basically my question is why can't these two attributes get their values directly from the first attribute that I define via "hand" passed to the init?
Any help would be appreciated, and more importantly, I think more than just solutions, it would help me even more to understand what I am misunderstanding about how attributes, instances, and instantiation and all that works so that I know where my thinking is wrong. Thanks!
import random
from random import shuffle
from collections import deque
class Deck():
def __init__(self):
self.ranks = {"Ace":14, "King":13, "Queen":12, "Jack":11, "10":10, "9":9, "8":8, "7":7, "6":6, "5":5, "4":4, "3":3, "2":2}
self.suites = ["Heart", "Diamond", "Spade", "Club"]
self.cards = []
def create_cards(self):
for suite in self.suites:
for key in self.ranks.keys():
self.cards.append(key + " " + suite)
def shuffle(self):
random.shuffle(deck.cards)
deck = Deck()
deck.create_cards()
deck.shuffle()
class Player():
def __init__(self, hand):
self.name = "name"
self.hand = hand
self.card_number = 1
self.card = self.hand[self.card_number].split()
self.card_val = deck.ranks.get(self.card[0])
def war(bool, p1, p2):
if bool == True:
for player in [p1, p2]:
player.card_number = 4
else:
for player in [p1, p2]:
player.card_number = 0
p2 = Player(deque(deck.cards[::2]))
p1 = Player(deque(deck.cards[1::2]))
p2.name = "The Computer"
def get_card_vals(p1, p2):
for player in [p1, p2]:
player.card = player.hand[player.card_number].split()
player.card_val = deck.ranks.get(player.card[0])
print("{a} vs. {b}".format(a = p1.card, b = p2.card))
print("---------------------------")
def cant_war_lose(winner, loser):
print("{a} doesn't have enough cards to go to war, so {b} wins the Battle!".format(a = loser, b = winner))
def battle_win(winner, loser):
print("{a} has run out of cards, therefore {b} has won via Battle!".format(a = loser, b = winner))
def play_cards(p1, p2):
war(False, p1, p2)
get_card_vals(p1, p2)
if p1.card_val > p2.card_val:
p1.hand.append(p2.hand.popleft())
p1.hand.rotate(-1)
elif p1.card_val == p2.card_val:
if len(p1.hand) < 5 or len(p2.hand) < 5:
if len(p1.hand) > len(p2.hand):
cant_war_lose(p1.name, p2.name)
else:
cant_war_lose(p2.name, p1.name)
return 0
else:
input("War is inititated! Press Enter to continue!")
print("---------------------------")
war(True, p1, p2)
get_card_vals(p1, p2)
if p1.card_val > p2.card_val:
for i in range(0,5):
p1.hand.append(p2.hand.popleft())
p1.hand.rotate(-5)
elif p1.card_val < p2.card_val:
for i in range(0,5):
p2.hand.append(p1.hand.popleft())
p2.hand.rotate(-5)
else:
p1.hand.rotate(-1)
p2.hand.rotate(-1)
elif p1.card_val < p2.card_val:
p2.hand.append(p1.hand.popleft())
p2.hand.rotate(-1)
if len(p1.hand) != 0 and len(p2.hand) != 0:
input("After the last round of Battle, {a} now has {b} cards, and {c} now has {d} cards! Press Enter to continue!".format(a = p1.name, b = len(p1.hand), c = p2.name, d = len(p2.hand)))
print("---------------------------")
else:
if len(p1.hand) > len(p2.hand):
battle_win(p1.name, p2.name)
else:
battle_win(p2.name, p1.name)
return 0
def game_run():
run = 1
p1.name = input("Player 1's name? ")
print("---------------------------")
while run == 1:
if play_cards(p1, p2) == 0:
run = 0
game_run()
You can use the property decorator to create a calculated property
class Player():
def __init__(self, hand):
self.name = "name"
self.hand = hand
self.card_number = 1
#property
def hand(self):
return self._hand
#hand.setter
def hand(self, value):
self._hand = value
self.card = self._hand[self.card_number].split()
self.card_val = deck.ranks.get(self.card[0])
What you misunderstand is variables, not instances. For instance, the attribute card is a scalar variable attached to the instance. Assigning to it with
self.card = <blah>
does not bind it to blah for constant recomputation. This is a value assignment, not a memory mapping. If you want that long-term binding, you must either write the maintenance routine yourself -- which you've already done, in a way, with the consistent recomputation -- or you must assign a mutable reference to self.card, so that card refers to teh same object as the expression you created.
Given that you are consistently rotating and altering the hand, this is not feasible in your design. Instead, simply write an access routine, perhaps get_next_card(hand), which will rotate the hand, extract the card, and return the desired rank and suit.
If you plan to program more card games, you will also find it handy to define a class card and class hand, with appropriate support routines. Maintain the card as a pair of integers; convert to strings only for printing.
Does that get you moving?
For anyone who wanted to compare a before and after of the problem & final solution, below is the working code for my specific issue. All I had to do was convert self.card and self.card_val to a calculated property. By passing in hand, and subsequently handling only hand, self.card & self.card_val are calculated, since every time I handle the instance of the class (by handling hand), these "method attributes" are being called and altered. Thanks for the input, guys!
class Player():
def __init__(self, hand):
self.name = "name"
self.card_number = 1
self.hand = hand
#property
def card(self):
return self.hand[self.card_number].split()
#property
def card_val(self):
return deck.ranks.get(self.card[0])

Monte Carlo Tree Search Tic-Tac-Toe -- Poor Agent

I'm trying to implement Monte Carlo tree search to play tic-tac-toe in Python. My current implementation is as follows:
I have a Board class that handles alterations to the tic-tac-toe board. The state of the board is represented by a 2x3x3 numpy array, where each of the 2 3x3 matrices are binary matrices individually representing the presence of X's and the presence of O's.
class Board:
'''
class handling state of the board
'''
def __init__(self):
self.state = np.zeros([2,3,3])
self.player = 0 # current player's turn
def copy(self):
'''
make copy of the board
'''
copy = Board()
copy.player = self.player
copy.state = np.copy(self.state)
return copy
def move(self, move):
'''
take move of form [x,y] and play
the move for the current player
'''
if np.any(self.state[:,move[0],move[1]]): return
self.state[self.player][move[0],move[1]] = 1
self.player ^= 1
def get_moves(self):
'''
return remaining possible board moves
(ie where there are no O's or X's)
'''
return np.argwhere(self.state[0]+self.state[1]==0).tolist()
def result(self):
'''
check rows, columns, and diagonals
for sequence of 3 X's or 3 O's
'''
board = self.state[self.player^1]
col_sum = np.any(np.sum(board,axis=0)==3)
row_sum = np.any(np.sum(board,axis=1)==3)
d1_sum = np.any(np.trace(board)==3)
d2_sum = np.any(np.trace(np.flip(board,1))==3)
return col_sum or row_sum or d1_sum or d2_sum
I then have a Node class, which handles properties of nodes as the search tree is being built:
class Node:
'''
maintains state of nodes in
the monte carlo search tree
'''
def __init__(self, parent=None, action=None, board=None):
self.parent = parent
self.board = board
self.children = []
self.wins = 0
self.visits = 0
self.untried_actions = board.get_moves()
self.action = action
def select(self):
'''
select child of node with
highest UCB1 value
'''
s = sorted(self.children, key=lambda c:c.wins/c.visits+0.2*sqrt(2*log(self.visits)/c.visits))
return s[-1]
def expand(self, action, board):
'''
expand parent node (self) by adding child
node with given action and state
'''
child = Node(parent=self, action=action, board=board)
self.untried_actions.remove(action)
self.children.append(child)
return child
def update(self, result):
self.visits += 1
self.wins += result
Finally, I have UCT function which pulls everything together. This function takes a Board object and builds the Monte Carlo search tree to determine the next best possible move from the given board state:
def UCT(rootstate, maxiters):
root = Node(board=rootstate)
for i in range(maxiters):
node = root
board = rootstate.copy()
# selection - select best child if parent fully expanded and not terminal
while node.untried_actions == [] and node.children != []:
node = node.select()
board.move(node.action)
# expansion - expand parent to a random untried action
if node.untried_actions != []:
a = random.choice(node.untried_actions)
board.move(a)
node = node.expand(a, board.copy())
# simulation - rollout to terminal state from current
# state using random actions
while board.get_moves() != [] and not board.result():
board.move(random.choice(board.get_moves()))
# backpropagation - propagate result of rollout game up the tree
# reverse the result if player at the node lost the rollout game
while node != None:
result = board.result()
if result:
if node.board.player==board.player:
result = 1
else: result = -1
else: result = 0
node.update(result)
node = node.parent
s = sorted(root.children, key=lambda c:c.wins/c.visits)
return s[-1].action
I've scoured this code for hours and simply can't find the error in my implementation. I've tested numerous board states and pitted two agents against each other, but the function returns poor actions for even the most simple of board states. What am I missing and/or what is wrong with my implementation?
edit: here is an example of how two agents might be implemented to play:
b = Board() # instantiate board
# while there are moves left to play and neither player has won
while b.get_moves() != [] and not b.result():
a = UCT(b,1000) # get next best move
b.move(a) # make move
print(b.state) # show state
The problem appears to be as follows:
Your get_moves() function does not check if the game is already over. It can generate a non-empty list of moves for states where someone has already won.
When creating a new Node, you also don't check if the game state is already over, so a non-empty list of untried_actions is created.
In the Selection and Expansion phases of the algorithm, you also don't check for terminal game states. Once the Expansion phase hits a node that contains a game state where someone already won, it will happily apply an extra action and create a new node for the tree again, which subsequent Selection phases will also happily keep going through.
For these nodes where the game continues being played after someone already won, result() can return an incorrect winner. It simply checks if the most recent player to make a move won, which is correct if you stop playing as soon as someone wins, but can be incorrect if you keep playing after someone already won. So, you propagate all kinds of incorrect results through the tree.
The simplest way to fix this will be to modify get_moves() such that it returns an empty list when the game is already over. Then, these nodes will always fail the if node.untried_actions != [] check, which means the expansion phase gets skipped altogether, and you move straight on to the Play-out phase where there is a proper check for terminal game states. This can be done as follows:
def get_moves(self):
"""
return remaining possible board moves
(ie where there are no O's or X's)
"""
if self.result():
return []
return np.argwhere(self.state[0] + self.state[1] == 0).tolist()

Python modules to plot game of life script

I wanted to know what the modules you recommend for plotting lists of coordinates on the X-Y plane are. The purpose is to implement Game of Life; I've tried matplotlib.pyplot but I've got some issues with axes ratio and in general it doesn't seem like the best way to plot some sort of animation. The main idea in the script is to plot the world in which the cells live, with a tick given from time.sleep.
import matplotlib.pyplot as plt
class Cell:
#Cell class, with coordinates and an ON-OFF state
def __init__(self,x=0,y=0,state=0):
self.state=state
self.x=x
self.y=y
self.coord=[self.x,self.y]
def __repr__(self):
if self.state:
return "*"
else:
return " "
#methods to turn on and off the cells:
def turn_on(self):
self.state=1
return self
def turn_off(self):
self.state=0
return self
class Mondo:
#the world in which the cells live:
def __init__(self,width=10,height=30,starting_cells=[]):
self.width=width
self.height=height
self.grid=[[Cell(i,j) for j in range(height)] for i in range(width)]
for cell in starting_cells:
self.grid[cell.x][cell.y]=cell
def __repr__(self):
#this method is needed just if I wanted to use just the print statement instead of matplotlib
return "_"*(2*self.height+3)+"\n"+"\n".join([" ".join(["|"]+[" "+str(cell)+" " for cell in self.grid[row]]+["|"]) for row in range(self.width)])+"\n"+"_"*(2*self.height+3)
def plotlib(self):
#this is the best I can do:
Xaxis=[cell.x for i in range(self.width) for cell in self.grid[i] if cell.state]
Yaxis=[cell.y for i in range(self.width) for cell in self.grid[i] if cell.state]
plt.axis([0,self.width, 0, self.height])
return plt.show(plt.scatter(Xaxis,Yaxis,c="000000", marker='s'))
def __getitem__(self,row):
return self.grid[row]
def pos_cell(self,coord):
#retrieves the cell given the coordinates
return self.grid[coord[0]][coord[1]]
def cell_state(self,coord):
#outputs the cell's state on given coord
return self.pos_cell(coord).state
def neighbors_state(self,coord,cont=0):
#sums the neghbors' cell state
cell=self.pos_cell(coord)
x,y=cell.x,cell.y
for hor in [-1,0,1]:
for ver in [-1,0,1]:
if not hor==ver==0 and (0<=x+hor<self.width and 0<=y+ver<self.height):
cont+=self.cell_state([(x+hor)%self.width,(y+ver)%self.height])
return cont
def alive_dead(self,coord):
#for each cell, tells wether it must switch its state or not
cell=self.pos_cell(coord)
if not cell.state:
if self.neighbors_state(cell.coord)==3:
return True
else:
return False
elif cell.state:
if self.neighbors_state(cell.coord) in [2,3]:
return True
else:
return False
#NB starting cells must be given as a list, not as a grid
def game_of_life(width,height,starting_cells):
#the heart of the script
test=[[Cell(i,j) for j in range(height)] for i in range(width)]
world=Mondo(width,height,starting_cells)
world.plotlib()
for i in range(world.width):
for j in range(world.height):
if world.alive_dead([i,j]):
test[i][j].turn_on()
starting_cells=[test[i][j] for i in range(width) for j in range(height)]
plt.pause(0.5)
return game_of_life(width,height,starting_cells)
#Example of an oscillator:
#print game_of_life(10,10,[Cell(1,2,1),Cell(2,3,1),Cell(3,1,1),Cell(3,2,1),Cell(3,3,1)])
#Example of the Gosper gun:
gosper=[[Cell(i,j) for j in range(70)] for i in range(30)]
for i in range(len(gosper)):
for j in range(len(gosper[0])):
if [i,j] in [[1,26],[2,24],[2,26],[3,14],[3,15],[3,22],[3,23],[3,36],[3,37],[4,36],[4,37],[4,22],[4,23],[4,13],[4,17],[5,12],[5,18],[5,22],[5,23],[5,1],[5,2],[6,1],[6,2],[6,12],[6,16],[6,18],[6,19],[6,24],[6,26],[7,26],[7,18],[7,12],[8,13],[8,17],[9,14],[9,15]]:
gosper[i][j].turn_on()
gosp=[gosper[i][j] for i in range(len(gosper)) for j in range(len(gosper[0])) if gosper[i][j].state]
print game_of_life(30,70,gosp)
#Mondo(30,70,gosp).plotlib()
#print Mondo(30,70,gosp)
I gave a look at the matplotlib.animation but maybe there's too much stuff for me to use just for a Uni assignment.
Is there some module I can use to print some simple animation? Should I try harder with matplotlib.pyplot, and if so, how can I adjust axes ratio and stuff?
SUrfing the questions I found the module graphics, may it be useful?
Thank you

Categories