Python minimax for tictactoe - python

After completely failing the minimax implementation for tic tac toe, I fail to see what's wrong. Right now, my AI just goes around in a circle...
import collections
class InvalidLocationError(Exception): pass
import copy
class Board(object):
def __init__(self, board=None):
if board is None:
self.clear()
else:
self._board = board[:]
def place(self, i, row, column):
if not ((0 <= row <= 2) and (0 <= column <= 2)):
raise InvalidLocationError("Invalid Location.")
if self._board[row][column]:
raise InvalidLocationError("There's already a piece there")
self._board[row][column] = i
return self.checkVictory()
def check(self, row, column):
return self._board[row][column]
def checkVictory(self, board=None):
if board is None:
board = self._board
draw = True
for i in xrange(3):
r = self.rowcount(i)
c = self.colcount(i)
if i < 3:
d = self.diagcount(i)
else:
d = {0: 0, 1: 0, 2: 0}
for j in xrange(1, 3):
if d[j] == 3 or r[j] == 3 or c[j] == 3:
return j
if r[0] > 0 or c[0] > 0:
draw = False
if draw:
return -1
return 0
def rowcount(self, row):
return collections.Counter(self._board[row])
def colcount(self, col):
return collections.Counter([self._board[i][col] for i in xrange(3)])
def diagcount(self, left=True):
if left:
a = [self._board[0][0], self._board[1][1], self._board[2][2]]
else:
a = [self._board[0][2], self._board[1][1], self._board[2][0]]
return collections.Counter(a)
def clear(self):
self._board = ([0, 0, 0], [0, 0, 0], [0, 0, 0])
def __str__(self):
return "\n".join(map(lambda x: " ".join(map(lambda y : str(y), x)), self._board))
#staticmethod
def flipPiece(p):
return int(not (p - 1)) + 1
class AI(object):
class Node(object):
def __init__(self, board, nextMove):
self.board = board
self.nextMove = nextMove
self.paths = []
self.score = None
template = self.board._board[:]
for r, row in enumerate(template):
for c, val in enumerate(row):
if val == 0:
template[r][c] = nextMove
self.paths.append(copy.deepcopy(template))
template[r][c] = 0
def __init__(self, mypiece, depth=8):
self.mypiece = mypiece
self.enemypiece = Board.flipPiece(mypiece)
self.depth = depth
def decide(self, board):
startNode = self.Node(board, self.mypiece)
best = self.minimax(startNode, self.depth)
for node in startNode.paths:
if node.value == best:
break
found = False
for row in xrange(3):
for col in xrange(3):
if board.check(row, col) != node.board.check(row, col):
found = True
break
if found:
break
print row, col
return row, col
def minimax(self, node, depth):
victory = node.board.checkVictory()
if victory:
if victory == self.mypiece:
h = 1
elif victory == -1:
h = 0
else:
h = -1
node.value = h
return h
if depth <= 0:
# h = self.heuristic(node.board, node.nextMove) # This is to the heuristic, which uses nextMove to evalate.
node.value = 0
return 0
h = -1
flip = Board.flipPiece(node.nextMove)
for i, board in enumerate(node.paths):
node.paths[i] = self.Node(Board(board), flip) # This is to the Node, which takes the nextMove of itself (which translates to the next next move from the current node)
score = self.minimax(node.paths[i], depth-1)
h = max(h, score) if node.nextMove == self.mypiece else min(h, score)
node.value = h
return h
Why is this happening?

Related

How to make Minimax function run for multiple piles

Here is my brief: 'Nimsticks is a turn-taking zero-sum game for two players. The game state consists of one or more piles of sticks. On their turn, a player may take 1, 2 or 3 sticks from any one pile. The player who takes the last stick is the loser of the game.
The problem is my code works fine when there is only one pile ([4], 1) but if I run it with multiple piles ([2, 3], 1) the if statements maxValue < v and minValue > v aren’t triggered (which they always should be due to inf) and are causing an error. Please help!
import copy
import math
def minimax_value(state):
if (state[0] == []):
return(result(state))
else:
if (state[1] == 1):
v, path = (max_value(state))
else:
v, path = (min_value(state))
optimalPath = path
optimalPath.append(state)
optimalPath.reverse()
print('Example play: ' + str(optimalPath))
return str(v)
def terminal(state):
if (state[0] == []):
return True
else:
return False
def utility(state):
if (state[1] == 1):
return(1, [])
else:
return(-1, [])
def max_value(state):
if terminal(state):
return(utility(state))
else:
v = -1 * math.inf
for c in next_states(state):
minValue, minPath = min_value(c)
if (minValue > v):
v = minValue
bestPath = minPath.copy()
bestSucc = c
bestPath.append(bestSucc)
return(v, bestPath)
def min_value(state):
if terminal(state):
return(utility(state))
else:
v = 1 * math.inf
for c in next_states(state):
maxValue, maxPath = max_value(c)
if (maxValue < v):
v = maxValue
bestPath = maxPath.copy()
bestSucc = c
bestPath.append(bestSucc)
return(v, bestPath)
def next_states(state):
player = state[1]
piles = copy.deepcopy(state[0])
pilesSave = copy.deepcopy(piles)
temp = []
newList = []
for x in range(0, len(piles)):
for y in range(1, 4):
piles[x] = piles[x] - y
if (piles[x] >= 0):
temp = copy.deepcopy(piles)
if (piles[x] == 0):
del temp[x]
newList.append(temp)
break
newList.append(temp)
piles[x] = pilesSave[x]
newList2 = []
for item in newList:
if (player == 1):
toAdd = (item, 2)
else:
toAdd = (item, 1)
newList2.append(toAdd)
return(newList2)
print(minimax_value(([2, 3], 1)))

Can't find error in chess code. When piece is moved back it doesn't update the position

I'm making chess in python. It was all working and I had the moves as a list of indexes but I decided to use a class instead as I thought this would be better. However, now the function to get all the legal moves doesn't seem to work and I've found that this is because for some reason it thinks it can move a pawn from a4 to a5 on the starting board and I think this could be because it doesn't change the position back to the normal position after checking a2 to a4 but does move it on the board which is made up of a list of lists however I cannot find why. The error is caused by the code in lines 379-387 which stops you making a move if it puts you in check because when I comment it out there's no error. I've been searching for hours so if someone could help then it would be much appreciated. Also any other feedback would be helpful.
Exceptions.py
# Base class for other exceptions
pass
class NoPieceThereError(Error):
# When there's no piece on the selected tile
pass
class InvalidFENError(Error):
# When the inputed FEN is invalid
pass
class WrongPieceColour(Error):
# Chose the wrong piece colour
pass
class InvalidMove(Error):
# Not a legal move
pass
class InvalidInput(Error):
pass
class KingNotFound(Error):
pass
Chess.py
import random
alphabet = "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z".split()
def IsInt(number):
try:
int(number)
return True
except:
return False #returns True if the input is a number
def AddTuples(tuple1, tuple2):
return tuple([x + y for x, y in zip(tuple1, tuple2)])
def MultiplyTuple(tuple1, multiplyer):
return tuple([int(x * multiplyer) for x in tuple1])
def PositionToIndex(position):
try:
row = int(position[1]) - 1
column = alphabet.index(position[0].upper())
return (row, column) # Converts a position "e2" to an index "(1, 4)"
except:
raise Exceptions.InvalidInput
def IndexToPosition(index):
try:
row = str(index[0] + 1)
column = alphabet[index[1]].lower()
return column + row # Converts an index "(1, 4)" to a position "e2"
except:
raise Exceptions.InvalidInput
class FEN():
def __init__(self, passed_FEN):
passed_FEN = passed_FEN.split()
if len(passed_FEN) != 6:
raise Exceptions.InvalidFENError
self.board_FEN = passed_FEN[0]
self.turn = passed_FEN[1]
self.castling = passed_FEN[2]
self.en_passant = passed_FEN[3]
self.half_move_clock = passed_FEN[4]
self.full_move_clock = passed_FEN[5]
def __repr__(self):
return " ".join([
self.board_FEN,
self.turn,
self.castling,
self.en_passant,
self.half_move_clock,
self.full_move_clock
])
def ChangeTurn(self):
self.turn = "b" if self.turn == "w" else "w"
#property
def board_FEN_split(self):
return self.board_FEN.split("/")
class Move():
def __init__(self, start_row, start_column, end_row, end_column, capturing_piece, capturing_row, capturing_column, FEN_before):
self.start_row = start_row
self.start_column = start_column
self.end_row = end_row
self.end_column = end_column
self.capturing = capturing_piece
self.capturing_row = capturing_row
self.capturing_column = capturing_column
self.FEN_before = FEN_before
#property
def start(self):
return (self.start_row, self.start_column)
#property
def end(self):
return (self.end_row, self.end_column)
#property
def capturing_pos(self):
return (self.capturing_row, self.capturing_column)
def __repr__(self):
if self.capturing != None:
string = f" capturing {self.capturing.__repr__()} at {IndexToPosition(self.capturing_pos)}"
else:
string = ""
return f"Piece moved from {IndexToPosition(self.start)} to {IndexToPosition(self.end)}" + string + f", FEN before: {self.FEN_before}"
def __eq__(self, other):
return True if self.__repr__() == other.__repr__() else False
#property
def taking_equals_moved(self):
return True if None == self.capturing_row == self.capturing_column or self.capturing_row == self.end_row and self.capturing_column == self.end_column else False
"""
class Engine():
piece_values = {
"p": 1,
"n": 3,
"b": 3,
"r": 5,
"q": 9
}
#staticmethod
def EvaluatePosition(board):
total = 0
for row in board.board:
for cell in row:
if cell != None:
if cell.__repr__().lower() in Engine.piece_values:
if cell.__repr__().islower():
total -= Engine.piece_values[cell.__repr__()]
else:
total += Engine.piece_values[cell.__repr__().lower()]
return total
#staticmethod
def ChooseComputerMove(board):
move = random.choice(board.GetAllLegalMoves())
board.MovePiece(*move)
#staticmethod
def Search(board, depth=10, alpha=0, beta=0):
if depth == 0:
return Engine.EvaluatePosition(board)
moves = board.GetAllLegalMoves()
if len(moves) == 0:
if board.IsInCheck(board.FEN.turn):
return -10000
return 0
for move in moves:
board.MovePiece(*move)
evaluation = -Engine.Search(board, depth-1, -beta, -alpha)
board.UnmakeMove()
if evaluation >= beta:
# Move was too good so the opponent will avoid this position
return beta
alpha = max(alpha, evaluation)
return alpha
"""
class Board():
def __init__(self,
passed_FEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
extra_pieces = [],
board_height = 8,
board_width = 8
):
pieces = [
#default pieces
('p', 'Pawn', Pawn),
('r', 'Rook', Rook),
('n', 'Knight', Knight),
('b', 'Bishop', Bishop),
('q', 'Queen', Queen),
('k', 'King', King),
] + extra_pieces #any extra piece types
self.pieces_letters, self.pieces_names, self.pieces_classes = [tuple([piece[x] for piece in pieces]) for x in range(3)]
self.FEN = FEN(passed_FEN)
self.move_list = []
self.board = []
self.board_height = board_height
self.board_width = board_width
for row_index, row in enumerate(self.FEN.board_FEN_split[::-1]):
self.board.append([])
cell_index = 0
for cell in row:
if IsInt(cell):
[self.board[-1].append(None) for x in range(int(cell))]
cell_index += int(cell)
else:
self.board[-1].append(self.CreatePiece(cell, (row_index, cell_index)))
cell_index += 1
def CreatePiece(self, piece_code, position):
piece_name = self.pieces_classes[self.pieces_letters.index(piece_code.lower())]
colour = "w" if piece_code.upper() == piece_code else "b"
return piece_name(colour, position, self)
def DisplayBoard(self, debug = False):
print()
print()
for index, column in enumerate(self.board[::-1]):
if debug:
print(7 - index, end = " ")
else:
print(8 - index, end = " ")
print(" | ".join(" " if x == None else x.__repr__() for x in column))
print("-" * 4 * self.board_width)
print(" ", end = "")
if debug:
print(" | ".join([str(x) for x in range(self.board_width)]))
else:
print(" | ".join(alphabet[:self.board_width]))
def GetBoardValue(self, row, column):
return self.board[row][column]
def FindKing(self, colour):
for rowi, row in enumerate(self.board):
for celli, cell in enumerate(row):
if cell != None:
if cell.type == "King" and cell.colour == colour:
return (rowi, celli)
raise Exceptions.KingNotFound
def IsInCheck(self, colour):
for row in self.board:
for cell in row:
if cell != None:
if cell.colour != colour:
if cell.IsChecking():
return True
return False
def GetAllLegalMoves(self):
LegalMoves = []
for row in self.board:
for cell in row:
if cell != None:
if cell.colour == self.FEN.turn:
LegalMoves += cell.LegalMoves()
return LegalMoves
def FindMove(self, moves = None, start_row = "*", start_column = "*", end_row = "*", end_column = "*"):
if moves == None:
moves = self.GetAllLegalMoves()
for move in moves:
if (
(start_row == "*" or start_row == move.start_row) and
(start_column == "*" or start_column == move.start_column) and
(end_row == "*" or end_row == move.end_row) and
(end_column == "*" or end_column == move.end_column)
):
return move
raise Exceptions.InvalidInput
def MakeMove(self, move):
self.move_list.append(move)
# Changing en passant FEN
if self.board[move.start_row][move.start_column].__repr__().lower() == "p" and abs(move.start_row - move.end_row) == 2:
self.FEN.en_passant = IndexToPosition(((move.start_row + move.end_row) / 2, move.end_column))
else:
self.FEN.en_passant = "-"
# Move piece
print(move)
self.board[move.start_row][move.start_row].position = move.end
self.board[move.end_row][move.end_column] = self.board[move.start_row][move.start_column]
self.board[move.start_row][move.start_column] = None
# If taking a piece not at the location it moved to i.e. en passant
if not move.taking_equals_moved:
self.board[capturing_row][capturing_column] = None
def UnmakeMove(self, number_to_undo=1):
for x in range(number_to_undo):
if self.move_list == []:
print(f"No more moves to undo. Only undid {x} moves")
return
move = self.move_list.pop()
# Move piece back
self.board[move.end_row][move.end_column].position = move.start
self.board[move.start_row][move.start_column] = self.board[move.end_row][move.end_column]
self.board[move.end_row][move.end_column] = None
# Restore captured piece
if move.capturing != None:
self.board[move.capturing_row][move.capturing_column] = move.capturing
# Restore FEN
self.FEN = move.FEN_before
def OnBoard(self, row, column):
return True if 0 <= row < self.board_height and 0 <= column < self.board_width else False
class Piece():
def __init__(self, colour, position, board):
self.colour = colour
self.position = position
self.board = board
def SimilarMoves(self, directions, *args, **kwargs):
legal_moves = []
for direction in directions:
legal_moves += self.CheckForCollision(direction, *args, **kwargs)
return legal_moves
def CheckForCollision(self, direction, limit, must_capture = False, can_capture = True, can_capture_en_passant = False, current_turn = False):
legal_moves = []
checks = 1
current_pos = self.position
while checks <= limit or limit == 0:
current_pos = AddTuples(current_pos, direction)
#if off the board
if not self.board.OnBoard(*current_pos):
break
current_pos_piece = self.GetBoardValue(*current_pos)
#if moving to an empty square
if current_pos_piece == None:
#capturing en passant
#if there is an en passant somewhere and the piece is allowed to capture en passant
if can_capture_en_passant and self.board.FEN.en_passant != "-":
#if it's capturing the en passant square
if PositionToIndex(self.board.FEN.en_passant) == current_pos:
#if it not capturing it's own piece
en_passant_start = self.board.board_height - 4 if self.colour == "w" else 3
if self.position[0] == en_passant_start:
move = Move(
*self.position,
*current_pos,
self.GetBoardValue(en_passant_start, current_pos[1]),
en_passant_start, current_pos[1],
FEN(self.board.FEN.__repr__()))
legal_moves.append(move)
#moving to empty space
elif not must_capture:
legal_moves.append(Move(
*self.position,
*current_pos,
self.GetBoardValue(*current_pos),
None, None,
FEN(self.board.FEN.__repr__())))
#moving to own piece colour
elif self.colour == current_pos_piece.colour:
break
#moving to opponent piece colour
elif self.colour != current_pos_piece.colour:
if can_capture:
legal_moves.append(Move(
*self.position,
*current_pos,
self.board.GetBoardValue(*current_pos),
*current_pos,
FEN(self.board.FEN.__repr__())
))
break
checks += 1
if current_turn == True:
index = 0
while index < len(legal_moves):
self.board.MakeMove(legal_moves[index])
if self.board.IsInCheck(self.colour):
legal_moves.pop(index)
else:
index += 1
self.board.UnmakeMove()
return legal_moves
#property
def row(self):
return self.position[0]
#property
def column(self):
return self.position[1]
def __repr__(self):
letter = self.board.pieces_letters[self.board.pieces_names.index(self.type)]
return letter.upper() if self.colour == "w" else letter
def IsEmpty(self, row, column):
return True if self.GetBoardValue(row, column) == None else False
def GetBoardValue(self, row, column):
return self.board.GetBoardValue(row, column)
#property
def direction(self):
if self.colour == "w":
return 1
else:
return -1
#property
def enemy_colour(self):
if self.colour == "w":
return "b"
else:
return "w"
def CheckCorrectTurn(self):
return True if self.colour == self.board.FEN.turn else False
def IsChecking(self):
enemy_king_pos = self.board.FindKing(self.enemy_colour)
moves = self.LegalMoves(current_turn = False)
for move in moves:
if move == enemy_king_pos:
return True
return False
class Pawn(Piece):
def __init__(self, colour, position, board):
super().__init__(colour, position, board)
self.type = "Pawn"
def LegalMoves(self, current_turn = True):
legal_moves = []
print(self.position)
on_starting_row = self.row == 1 and self.colour == 'w' or self.row == self.board.board_height - 2 and self.colour == "b"
#forwards
limit = 2 if on_starting_row else 1
legal_moves += self.CheckForCollision((self.direction, 0), limit, can_capture = False, current_turn=current_turn)
#capturing
capturing_directions = ((self.direction, 1), (self.direction, -1))
legal_moves += self.SimilarMoves(capturing_directions, 1, must_capture = True, can_capture_en_passant = True, current_turn=current_turn)
return legal_moves
class Rook(Piece):
directions = ((1, 0), (0, 1), (-1, 0), (0, -1))
def __init__(self, colour, position, board):
super().__init__(colour, position, board)
self.type = "Rook"
def LegalMoves(self, current_turn = True):
legal_moves = self.SimilarMoves(self.directions, 0, current_turn=current_turn)
return legal_moves
class Knight(Piece):
directions = ((2, 1), (1, 2), (-1, 2), (-2, 1), (-2, -1), (-1, -2), (1, -2), (2, -1))
def __init__(self, colour, position, board):
super().__init__(colour, position, board)
self.type = "Knight"
def LegalMoves(self, current_turn = True):
legal_moves = self.SimilarMoves(self.directions, 1, current_turn=current_turn)
return legal_moves
class Bishop(Piece):
directions = ((1, 1), (-1, 1), (-1, -1), (1, -1))
def __init__(self, colour, position, board):
super().__init__(colour, position, board)
self.type = "Bishop"
def LegalMoves(self, current_turn = True):
legal_moves = self.SimilarMoves(self.directions, 0, current_turn=current_turn)
return legal_moves
class King(Piece):
directions = Rook.directions + Bishop.directions
def __init__(self, colour, position, board):
super().__init__(colour, position, board)
self.type = "King"
def LegalMoves(self, current_turn = True):
legal_moves = self.SimilarMoves(self.directions, 1, current_turn=current_turn)
return legal_moves
class Queen(Piece):
directions = Rook.directions + Bishop.directions
def __init__(self, colour, position, board):
super().__init__(colour, position, board)
self.type = "Queen"
def LegalMoves(self, current_turn = True):
legal_moves = self.SimilarMoves(self.directions, 0, current_turn=current_turn)
return legal_moves
def main():
entered_FEN_valid = False
while not entered_FEN_valid:
try:
player_FEN = input("Would you like to play with a custom FEN. If so put it here, or press enter for a default board: ")
if not player_FEN:
player_FEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
b = Board(player_FEN)
entered_FEN_valid = True
except Exceptions.InvalidFENError:
print("Invalid FEN")
#no longer needed as I added Smith --> print("Moves are entered in the form \"{row} {column}\" with row and column being numbers from 0-7. Bottom left is 0 0. Example input: \"1 4\"")
debug = False # True if input("Debug Mode? (Y/N): ").lower() == "y" else False
player_colour = "w"
player_won = False
apawn = b.board[1][0]
while not player_won:
#if len(b.GetAllLegalMoves()) == 0:
# player_won = True
# print(b.IsInCheck("b"))
# if b.IsInCheck(b.FEN.turn):
# player_who_won = "w" if b.FEN.turn == "b" else "b"
# else:
# player_who_won = "no one"
# break
try:
if True: #b.FEN.turn == player_colour:
b.DisplayBoard(debug)
#print(Engine.Search(b, depth = 20))
#print("\n".join([x.__repr__() for x in b.move_list]))
#print([[IndexToPosition(position) for position in move] for move in b.GetAllLegalMoves()])
#print(len(b.GetAllLegalMoves()))
start = input("\nEnter start: ")
if start.lower()[:4] == "undo":
b.UnmakeMove(int(start[-1]))
else:
start = PositionToIndex(start)
if b.GetBoardValue(*start) == None:
raise Exceptions.NoPieceThereError
moves = b.GetBoardValue(*start).LegalMoves()
if moves == []:
raise Exceptions.WrongPieceColour
print("Legal moves: \n" + str([IndexToPosition((x.end_row, x.end_column)) for x in moves]))
end = PositionToIndex(input("\nEnter end: "))
print(start, end)
b.MakeMove(b.FindMove(
moves = moves,
start_row = start[0],
start_column = start[1],
end_row = end[0],
end_column = end[1]))
else:
Engine.ChooseComputerMove(b)
except Exceptions.NoPieceThereError:
print("There is no piece there")
except Exceptions.WrongPieceColour:
print("That is not the correct piece colour")
except Exceptions.InvalidMove:
print("That is not a legal move")
#except Exceptions.InvalidInput:
# print("Invalid input")
#except:
# print("Unknown error")
print(player_who_won, "won!")
if __name__ == "__main__":
#main()
b = Board()
print(b.GetAllLegalMoves())
So I found that the positions of the pieces kept changing randomly so I just made it a property of the Piece class that finds it each time
class Piece():
#property
def position(self):
for rowi, row in enumerate(self.board.board):
for celli, cell in enumerate(row):
if cell == self:
return (rowi, celli)

N Puzzle with Depth First Search

I'm trying solve N Puzzle with Depth First Search using python 3.
With 3 x 3 puzzle it run good and fast but with 4 x 4 puzzle, it runs too slow and can't find solution with error: "MemoryError".
I also use "h(n) = depth + number of wrong tiles" to evaluate priority of each node.
I'm a newbie to python so hope you can help me with this
Here is my code:
import sys
import getopt
import random
import time
class State:
def __init__(self, parent, board, move, depth):
self.parent = parent
self.previousMove = move
self.board = board
self.map = ''.join(str(e) for e in board)
self.depth = depth
self.cost = self.calculateCost()
def calculateCost(self):
pos = 1
count = 0
for tile in self.board:
if tile == pos:
count += 1
pos += 1
return self.depth + 8 - count
class Puzzle:
def __init__(self, k, customBoard = None):
self.k = k
self.n = k*k - 1
self.sizeOfBoard = k*k
self.timeOfSolving = 0
self.timeOfGenerateSuccessors = 0
self.maxDeepSearch = 0
self.inititalState = State(None, self.createInitialBoard(customBoard), 'Start', 0)
self.goalBoard = self.createGoalBoard()
self.finalState = None
self.stateStorage = set() # Store states that have visited
self.path = [] # Store states that lead to goalstate
self.stack = []
def isSolvable(self, board):
# count invertion in puzzle's board
invCount = 0
for i in range(0, self.sizeOfBoard - 1):
if board[i] == 0:
continue
for j in range(i+1, self.sizeOfBoard):
if board[j] == 0:
continue
if board[i] > board[j]:
invCount += 1
# print(invCount)
if (invCount % 2 == 0):
return True
return False
def createInitialBoard(self, customBoard):
print("Creating initial state")
if customBoard is None:
board = []
lstAddSuccess = []
while 1:
board.clear()
lstAddSuccess.clear()
for count in range(0, self.k*self.k):
newTile = random.randint(0, self.n)
while newTile in lstAddSuccess:
newTile = random.randint(0, self.n)
lstAddSuccess += [newTile]
board += [newTile]
if self.isSolvable(board):
break
else:
board = [int(e) for e in customBoard]
if not self.isSolvable(board):
print("Cant find solution with this puzzle! Exiting...")
exit(-1)
return board
def createGoalBoard(self):
board = []
for count in range(1, self.n + 1):
board += [count]
board += [0]
return board
def printBoard(self, board):
for row in range(0, self.sizeOfBoard, self.k):
# for col in range(row, row + self.k):
print(board[row:row + self.k])
def generateSuccessors(self, currentState):
indexOfZero = currentState.board.index(0)
rowIndexOfZero = indexOfZero % self.k
colIndexOfZero = indexOfZero // self.k
lstSuccessors = []
# Slide to zero to up
if colIndexOfZero != 0:
newState = currentState.board.copy()
newState[indexOfZero] = newState[indexOfZero - self.k]
newState[indexOfZero - self.k] = 0
lstSuccessors.append(
State(currentState, newState, 'up', currentState.depth + 1))
# Slide zero to down
if colIndexOfZero != self.k - 1:
newState = currentState.board.copy()
newState[indexOfZero] = newState[indexOfZero + self.k]
newState[indexOfZero + self.k] = 0
lstSuccessors.append(
State(currentState, newState, 'down', currentState.depth + 1))
# slide zero to left
if rowIndexOfZero != 0:
newState = currentState.board.copy()
newState[indexOfZero] = newState[indexOfZero - 1]
newState[indexOfZero - 1] = 0
lstSuccessors.append(
State(currentState, newState, 'left', currentState.depth + 1))
# Slide zero to right
if rowIndexOfZero != self.k - 1:
newState = currentState.board.copy()
newState[indexOfZero] = newState[indexOfZero + 1]
newState[indexOfZero + 1] = 0
lstSuccessors.append(
State(currentState, newState, 'right', currentState.depth + 1))
lstSuccessorsCost = [ele.cost for ele in lstSuccessors]
lstSuccessorsInOrderOfCost = []
for i in range(0, len(lstSuccessorsCost)):
lstSuccessorsInOrderOfCost.append(lstSuccessors[lstSuccessorsCost.index(min(lstSuccessorsCost))])
lstSuccessorsCost[lstSuccessorsCost.index(min(lstSuccessorsCost))] = 100
return lstSuccessorsInOrderOfCost
def solvePuzzle(self, currentState):
self.stack.append(currentState)
self.stateStorage.add(currentState.map)
while len(self.stack) > 0:
currentState = self.stack.pop()
if currentState.board == self.goalBoard:
# find path
# self.printBoard(currentState.board)
self.finalState = currentState
print("Solving " + str(self.n) + " puzzle done!")
return
start_time_gen = time.time()
lstSuccessor = self.generateSuccessors(currentState)
end_time_gen = time.time()
timeOfGen = end_time_gen - start_time_gen
self.timeOfGenerateSuccessors += timeOfGen
for successor in lstSuccessor[::-1]:
if successor.map not in self.stateStorage:
self.stack.append(successor)
self.stateStorage.add(successor.map)
if successor.depth > self.maxDeepSearch:
self.maxDeepSearch += 1
print("Cant solve puzzle! Exiting...")
exit(-1)
def solve(self):
start_time = time.time()
self.solvePuzzle(self.inititalState)
end_time = time.time()
self.timeOfSolving = end_time - start_time
print("Running time: " + str(self.timeOfSolving))
print("Max Search Dept: " + str(self.maxDeepSearch))
print("Final State Dept: " + str(self.finalState.depth))
def printInitialBoard(self):
self.printBoard(self.inititalState.board)
def printPath(self):
if self.finalState is None:
print("No solution found!")
return
path = []
state = self.finalState
while (state is not None):
if state.previousMove is not None:
path.append(state.previousMove)
state = state.parent
print("path: "),
print(path[::-1])
def main(argv):
# if (len(argv) != 1 or int(argv[0]) not in range(1, 10000)):
# print("Input must be k of integer, which is k*k matrix of puzzle")
# exit()
# eight_puzzle = Puzzle(int(argv[0]))
k = int(input("Enter size of k * k puzzle, k = "))
while k not in range(2, 100):
print("k must be in range 2 - 100")
k = int(input("Enter size of k * k puzzle, k = "))
print("""
Choose:
1. Randome puzzle
2. Custome puzzle
""")
file = input()
if int(file) == 1:
puzzle = Puzzle(k)
elif int(file) == 2:
board = input("Enter puzzle: ")
puzzle = Puzzle(k ,list(board.split(" ")))
puzzle.printInitialBoard()
puzzle.solve()
puzzle.printPath()
if __name__ == "__main__":
main(sys.argv[1:])

Python: Recursion problems

I am trying to make a sudoku solver that solves boards very quickly. At the moment my solver works on easy boards but never terminates on harder boards. I believe it has something to do with my recursion because easy boards do not require recursion and hard boards do. Any help is appreciated.
import sys
def rowno(i):
return i // 9
def colno(i):
return i % 9
def boxno(i):
return (i // 9 // 3 )*3 + (i // 3) % 3
def isNeighbor(i, j):
if rowno(i) == rowno(j) or colno(i) == colno(j) or boxno(i) == boxno(j):
return True
else:
return False
def getFileName():
if sys.platform == "win32":
filename = input("Filename? ")
else:
filename = sys.argv[-1]
return filename
solutionlist = []
class Board(object):
def __init__(self, puzzle):
self.puzzle = puzzle
self.board = [Cell(int(value), idx) for idx, value in enumerate(puzzle)]
self.change = False
def printAll(self):
print [cell.candidates for cell in self.board]
#return str(" ")
def update(self):
self.change = False
l = [cell for cell in self.board if len(cell.candidates) == 1]
for i in l:
for j in xrange(81):
if isNeighbor(i.dex, j) and i.dex != j:
old = self.board[j].candidates
self.board[j].delCandidate(i.value)
if len(old) != len(self.board[j].candidates):
self.change = True
def toString(self):
str1 = ''.join(str(e.value) for e in self.board)
return str1
def solved(self):
for cell in self.board:
if len(cell.candidates) != 1:
return False
return True
def solve(self):
self.change = True
while self.change == True:
self.update()
if self.solved():
solutionlist.append(self.board)
return
l = [cell for cell in self.board if len(cell.candidates) > 1]
for i in l:
for j in i.candidates:
newBoard = Board(self.toString())
curLen = 12
curCell = -1
for u in l:
if len(u.candidates)<curLen:
curLen=len(u.candidates)
curCell = u.dex
for c in newBoard.board[curCell].candidates:
newBoard.board[curCell].candidates = [int(c)]
newBoard.board[curCell].value = int(c)
newBoard.solve()
return
def __repr__(self):
l = [cell.value for cell in self.board]
return str(l)
class Cell(object):
def __init__(self, value, dex):
self.value = value
self.dex = dex
if value == 0:
self.candidates = [1,2,3,4,5,6,7,8,9]
else:
self.candidates = [int(value)]
def __str__(self):
return str(self.value)
def delCandidate(self, value):
# deletes value from candidate list
#return self.candidate.remove(value);
self.candidates = [x for x in self.candidates if x != value]
if len(self.candidates) == 1:
self.value = self.candidates[0]
easy = "700583006006001405052006083300200958500078060648010300060802500003150072215600030"
twosol = "000805200800000401705040009000100702040000000006430000030900000010006080000000000"
hard = "040090008000000070060000120030020000005839060080600700050170600000043000003000200"
#easy solution: 794583216836721495152496783371264958529378164648915327967832541483159672215647839
b = Board(hard)
print b
b.solve()
print "end of the line"
for i in solutionlist:
print [cell.value for cell in i]
print "\n"
One major issue is the line for i in l: in the solve method. Since you're recursing, you only need to fill in one cell - the recursion will take care of the rest. So instead of for i in l:, just recurse on the one cell that is the best candidate (curCell):
l = [cell for cell in self.board if len(cell.candidates) > 1]
if len(l) > 0:
newBoard = Board(self.toString())
curLen = 12
curCell = -1
for u in l:
if len(u.candidates)<curLen:
curLen=len(u.candidates)
curCell = u.dex
for c in newBoard.board[curCell].candidates:
newBoard.board[curCell].candidates = [int(c)]
newBoard.board[curCell].value = int(c)
newBoard.solve()

Python Old object information being kept

I'm making a class of rooted Trees using a class of Nodes that have a "word" of the form [1,1,2], where [1,1] is the parent of [1,1,2] and [1,1,1] is the preceding sibling of [1,1,2], a list of children, and a parent. For some reason, in the for loop section, the second time the line nextChild = Node(word) it is taking as input one child (which is the previous nextChild) even though I am not passing in anything as the child. I have no idea why this is happening. I'll post more code if requested.
Edit: Here is the whole Tree.py file
import pdb
class Word:
def __init__(self, intList = []):
self.intList = intList
def __len__(self):
return len(self.intList)
def __getitem__(self, i):
if i < len(self):
return self.intList[i]
else:
raise AttributeError
def __str__(self):
if len(self.intList) == 0:
return "<e>"
selfStr = "<"
for i, val in enumerate(self.intList):
selfStr = selfStr + str(val)
selfStr = selfStr + ("" if i == (len(self.intList) - 1) else ", ")
selfStr = selfStr + ">"
return selfStr
def compare(self, word2):
shortestLength = len(self) if len(self) < len(word2) else len(word2)
for i in xrange(shortestLength):
if self[i] < word2[i]:
return -1
elif self[i] > word2[i]:
return 1
return -1 if len(self) < len(word2) else 1 if len(self) > len(word2) else 0
def isPrefixOf(self, word2):
if len(self) == 0 and len(word2) == 1:
return True
if len(self) != len(word2) + 1:
return False
for i in xrange(len(self)):
if self[i] != word2[i]:
return False
return True
class Node:
def __init__(self, word = Word(), children = [], parent = -1):
self.label = word
self.children = children
self.parent = parent
self.currentChild = 0
for i, child in enumerate(self.children):
if (not self.label.isPrefixOf(child.label)):
raise ValueError("The node " + str(child.label) + " is not a valid child of " + str(self.label))
def __str__(self):
return str(self.label)
def addChild(self, child):
#check if these are valid brothers
if len(self.children) == 0 and child.label[len(child.label) - 1] != 1:
raise ValueError("The node " + str(child.label) + " is not a valid child of " + str(self.label))
elif len(self.children) != 0 and not self.children[len(self.children) - 1].isPerviousBrotherOf(child):
raise ValueError("The node " + str(child.label) + " is not a valid child of " + str(self.label))
#check if valid parent
if not self.label.isPrefixOf(child.label):
raise ValueError("The node " + str(child.label) + " is not a valid child of " + str(self.label))
self.children.append(child)
def isValidParentOf(self, node):
return self.label.isPrefixOf(node.label)
def isPerviousBrotherOf(self, word2):
if len(self) != len(word2):
return False
return self[len(self) - 1] == word2[len(word2) - 1] - 1
def getParent(self):
return self.parent
def setParent(self, parent):
self.parent = parent
def getNextChild(self):
if self.currentChild >= len(self.children):
return -1
else:
self.currentChild = self.currentChild + 1
return self.children[self.currentChild - 1]
def resetPosition(self):
self.currentChild = 0
def numChildren(self):
return len(self.children)
class Tree:
def __init__(self, intList):
if len(intList) == 0:
raise ValueError("Trees cannot have size zero.")
wordList = map(lambda x: Word(x), intList)
wordList = sort(wordList)
self.root = Node(wordList[0])
currentNode = self.root
for i in xrange(1, len(wordList)):
word = wordList[i]
nextChild = Node(word)
while (currentNode != -1 and not currentNode.isValidParentOf(nextChild)):
currentNode.resetPosition()
currentNode = currentNode.getParent()
if (currentNode == -1):
raise ValueError("The list of words " + map(str, wordList) + " is not a valid tree.")
currentNode.addChild(nextChild)
nextChild.setParent(currentNode)
currentNode = currentNode.getNextChild()
while (currentNode.getParent() != -1):
currentNode.resetPosition()
currentNode = currentNode.getParent()
currentNode.resetPosition()
self.root = currentNode
self.size = len(wordList)
self.current = self.root
def __str__(self):
outStr = ""
outStr = createString(self.root)
def createString(self, node):
outStr = "(" + str(node)
child = node.getNextChild()
while child != -1:
outStr += " " + createString(child) + ")"
return outStr + ")"
def sort(inList):
if len(inList) <= 1:
return inList
return merge(sort(inList[:len(inList)/2]), sort(inList[len(inList)/2:]))
def merge(list1, list2):
outlist = []
i = 0
j = 0
while (i < len(list1) or j < len(list2)):
if i >= len(list1):
while (j < len(list2)):
outlist.append(list2[j])
j = j + 1
elif j >= len(list2):
while (i < len(list1)):
outlist.append(list1[i])
i = i + 1
elif list1[i].compare(list2[j]) == -1:
outlist.append(list1[i])
i = i + 1
else:
outlist.append(list2[j])
j = j + 1
return outlist
And here is some test code
from Tree import Tree
t = Tree([[], [1], [2], [3], [1, 1], [1, 2], [2, 1], [3, 1], [3, 2], [3, 3]])
print str(t)

Categories