I am trying to write a program that takes in a matrix representing a board filled with 0s and 1s.
The goal is to find a path from the top left position to target position, using backtracking. You can only move up, down, left and right one space at a time. The code below raises
RecursionError: maximum recursion depth exceeded in comparison
Why is it causing the error? Is there a way to move both up, down, right and left without causing this error?
class Maze:
def __init__(self, maze, target):
self.x, self.y = target
self.b = maze
self.n = len(self.b)
self.sb = [[0 for _ in range(self.n)] for _ in range(self.n)]
def is_safe(self, row, col):
if 0 <= row < self.n and 0 <= col < self.n and self.b[row][col] == 1:
return True
return False
def find_path(self):
move_x = [1, -1, 0, 0]
move_y = [0, 0, 1, -1]
if not self.find_path_rec(move_x, move_y, 0, 0):
print("No path")
else:
self.print_maze()
def find_path_rec(self, move_x, move_y, curr_x, curr_y):
if curr_y == self.y and curr_x == self.x and self.b[self.x][self.y] == 1:
self.sb[curr_x][curr_y] = 1
return True
for i in range(4):
new_x = move_x[i] + curr_x
new_y = move_y[i] + curr_y
if self.is_safe(new_x, new_y):
self.sb[new_x][new_y] = 1
if self.find_path_rec(move_x, move_y, new_x, new_y):
return True
self.sb[new_x][new_y] = 0
return False
def print_maze(self):
for i in range(self.n):
for j in range(self.n):
print(self.sb[i][j], end="")
print()
maze = [[1, 1, 1, 1],
[1, 1, 0, 1],
[1, 0, 0, 1],
[1, 1, 0, 1]]
The Wikipedia maze generation algorithm page https://en.wikipedia.org/wiki/Maze_generation_algorithm has your answer
A disadvantage of the [recursive] approach is a large depth of recursion – in the worst case, the routine may need to recur on every cell of the area being processed, which may exceed the maximum recursion stack depth in many environments.
The alternative is to a use a stack to store your path so far rather than using recursion. Appending each move to the stack, and back-tracking when you hit a dead end.
I believe the problem is in this line:
if self.is_safe(new_x, new_y):
which should be:
if self.is_safe(new_x, new_y) and self.sb[new_x][new_y] == 0:
That is, don't revisit positions as you'll get into a circular motion and thus recursive stack overflow. Below is my rework of your code in my own thinking about your problem:
COL, ROW = (0, 1)
class Maze:
def __init__(self, maze, target):
self.target = target
self.maze = maze
self.visited = set()
def is_safe(self, position):
col, row = position
return 0 <= row < len(self.maze) and 0 <= col < len(self.maze[row]) and self.maze[row][col] == 1
def find_path(self):
start = (0, 0)
moves = [(1, 0), (-1, 0), (0, 1), (0, -1)]
self.visited.add(start)
return self.find_path_recursive(moves, start)
def find_path_recursive(self, moves, current):
if current == self.target and self.maze[self.target[ROW]][self.target[COL]] == 1:
return True
for move in moves:
new = move[COL] + current[COL], move[ROW] + current[ROW]
if self.is_safe(new) and new not in self.visited:
self.visited.add(new)
if self.find_path_recursive(moves, new):
return True
self.visited.remove(new)
return False
def print_maze(self):
for row in range(len(self.maze)):
for col in range(len(self.maze[row])):
print(1 if (col, row) in self.visited else 0, end="")
print()
if __name__ == '__main__':
maze = [
[1, 1, 1, 1],
[1, 1, 0, 1],
[1, 0, 0, 1],
[1, 1, 0, 1]
]
game = Maze(maze, (3, 3))
if game.find_path():
game.print_maze()
else:
print("No path")
My sense of coordinates my be different than yours, so take that into account when evaluating results.
OUTPUT
> python3 test.py
1111
0001
0001
0001
>
If we move the target to (1, 3), then we get:
> python3 test.py
1100
1100
1000
1100
>
I also changed your logic to potentially handle rectangular mazes instead of just square ones.
The error is in this function:
def is_safe(self, row, col):
if 0 <= row < self.n and 0 <= col < self.n and self.b[row][col] == 1:
return True
return False
The condition should include a check that the cell has not yet been visited:
if (0 <= row < self.n and 0 <= col < self.n and self.b[row][col] == 1
and self.sb[row][col] == 0):
# ^^^^^^^^^^^^^^^^^^^^^^^^^^
I would also advise to use names that are meaningful:
self.b should better be named self.maze
self.sb should better be named self.visited
Related
I have a tic tac toe game using a minimax algorithm for the computer "player." The Tkinter portion works, and the actual person player works correctly. However, when the computer is supposed to play, it gives the error: "NoneType is not subscriptable." I'm not sure why. Am I missing an input for one of my variables? Thank you in advance.
Here is my code:
from tkinter import *
import customtkinter
import random
import minimax
customtkinter.set_appearance_mode("Dark")
#creating CTk window for app
root = customtkinter.CTk()
#setting window width and height
root.geometry('500x300')
#Creating label
label = customtkinter.CTkLabel(master=root,
text="Tic Tac Toe",
width=120,
height=50,
font=("normal", 20),
corner_radius=8)
label.place(relx=0.25, rely=0.8, anchor=CENTER)
#Handling clicks
DEPTH=8
def clickbutton(r, c):
buttons[r][c]["text"]="X"
board[r][c]="X"
buttons[r][c]['state']=DISABLED
label = customtkinter.CTkLabel(master=root,
text=checkwin(board),
width=120,
height=25,
corner_radius=8)
label.place(relx=0.25, rely=0.9, anchor=CENTER)
computerplay()
DEPTH=DEPTH-1
#Button matrix
buttons = [
[0,0,0],
[0,0,0],
[0,0,0]]
#Matrix identifying whether buttons are active or inactive
board=[[0,0,0],[0,0,0],[0,0,0]]
for i in range(3):
for j in range(3):
buttons[i][j] = Button(height = 3, width = 6, font = ("Normal", 20),
command = lambda r = i, c = j : clickbutton(r,c))
buttons[i][j].grid(row = i, column = j)
def computerplay():
bestmove=minimax.minimax(board, DEPTH, 1)
buttons[bestmove[0]][bestmove[1]]['text']="O"
buttons[bestmove[0]][bestmove[1]]['state']=DISABLED
board[bestmove[0]][bestmove[1]]="O"
def checkwin(b):
score=minimax.evaluate(b)
if score==10:
return 'Computer won!'
elif score==-10:
return 'You won!'
else:
return 'Player vs. Computer'
root.mainloop()
My minimax code:
import math
def change_board(board):
#changes board into -1, 0, and 1s instead of X, O, and 0 (zero).
new_board = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
for i in range(3):
for j in range(3):
if board[i][j]=='X':
new_board[i][j]==-1
elif board[i][j]=='O':
new_board[i][j]==1
return new_board
def empty_cells(board):
cells=[]
for i in range(3):
for j in range(3):
if board[i][j]==0:
cells.append([i, j])
return False
def game_over(board):
#check for wins using evaluate
if evaluate(board)==10 or evaluate(board)==-10:
return True
#check for full board
if (not empty_cells(board)):
return True
return False
def evaluate(board):
#check score
if board[0][0]==board[1][1] and board[1][1]==board[2][2]:
if board[0][0]==-1:
return -10
elif board[0][0]==1:
return 10
if board[0][2]==board[1][1] and board[1][1]==board[2][0]:
if board[0][2]==-1:
return -10
elif board[0][2]==1:
return 10
for row in range(3):
if board[row][0]==board[row][1] and board[row][1]==board[row][2]:
if board[row][0]==-1:
return -10
elif board[row][0]==1:
return 10
for col in range(3):
if board[0][col]==board[1][col] and board[1][col]==board[2][col]:
if board[0][col]==-1:
return -10
elif board[0][col]==1:
return 10
def minimax(board, depth, player):
if player==1:
#1 is computer. -1 is human player.
best=[-1, -1, -math.inf]
else:
best=[-1, -1, math.inf]
if depth==0 or game_over(board):
score=evaluate(board)
return score
#checking scores of valid moves
for cell in empty_cells(board):
x, y = cell[0], cell[1]
board[x][y] = player
score = minimax(board, depth - 1, -player)
board[x][y] = 0
score[0], score[1] = x, y
if player == 1:
if score[2] > best[2]:
best = score
else:
if score[2] < best[2]:
best = score
return best
There are these issues with your minimax code:
In change_board you are not assigning anything to new_board cells, as you perform a comparison, not an assignment. Change new_board[i][j]==-1 to new_board[i][j]=-1 and similarly in the else clause.
In empty_cells you are not doing anything with the work done on cells but just return False. Surely the caller needs the cells list, so you should return it.
In evaluate you should also return a numerical value when no winning line was detected: the caller needs a number to compare it with the best score so far, so leaving it to just return None is not good. Add a return 0 at the bottom of the function.
In minimax you should always return the same structure, i.e. a list with three members. This is not done in the base case where you just return a number. To fix that, that base case should return [-1, -1, score]. Related to this, you should consider using a different name for when the score is a number (like here), or when the score is that list, like you do elsewhere in the code. This is confusing.
With those fixes it will work.
Still, there are some things you could improve, including:
Where you return True or return False based on a boolean condition, you can just return the evaluation of that condition. For instance, game_over can do return abs(evaluate(board))==10 or not empty_cells(board)
You can chain comparison operators, so like board[0][0]==board[1][1]==board[2][2]
Avoid code repetition in game_over: put the information of where the winning lines are in a data structure and then loop over it to only have the three-cell comparisons coded once.
Use tuples instead of lists when there is no need to mutate it, like for x/y coordinates.
With math.inf you're using a float where otherwise the score would be an integer. It is better practice to avoid that type mix. In this case you can use the value 11 instead.
Here is how it could look:
def change_board(board):
return [
[(cell == "O") - (cell == "X") for cell in row]
for row in board
]
def empty_cells(board):
return [(i, j) for i in range(3) for j in range(3) if board[i][j] == 0]
def game_over(board):
return abs(evaluate(board)) == 10 or not empty_cells(board)
lines = (
(0, 0, 1, 1, 2, 2),
(0, 2, 1, 1, 2, 0),
(0, 0, 0, 1, 0, 2),
(1, 0, 1, 1, 1, 2),
(2, 0, 2, 1, 2, 2),
(0, 0, 1, 0, 2, 0),
(0, 1, 1, 1, 2, 1),
(0, 2, 1, 2, 2, 2),
)
def evaluate(board):
def iswin(line):
arow, acol, brow, bcol, crow, ccol = line
if board[arow][acol] == board[brow][bcol] == board[crow][ccol]:
return board[arow][acol]*10
return next((win for win in map(iswin, lines) if win), 0)
def minimax(board, depth, player):
best = (-1, -1, -11*player)
if depth <= 0 or game_over(board):
return (-1, -1, evaluate(board)) # must return same structure
for x, y in empty_cells(board):
board[x][y] = player
score = minimax(board, depth - 1, -player)[2] # only get the score
board[x][y] = 0
if (player == 1) == (score > best[2]):
best = (x, y, score) # only inject x, y when move is best
return best
More optimisation is possible when you use a bit representation of the board instead of a 2D matrix.
I am trying to create a tic tac toe game with an adjustable game size and a computer that uses the minimax algorithm. The game sizes can only be odd numbers, to make sure that diagonal wins are always possible. The game runs with no errors, but I know that the minimax algorithm isn't working 100% correct because I can still beat the computer. I've looked extensively over my code and cannot find where the algorithm is going wrong. Here is my code:
Main.py
import TicTacToe
import Minimax
if (__name__ == "__main__"):
t = TicTacToe.ttt(3)
m = Minimax.Minimax(3, t)
while (t.winner == None):
if (t.turn == 1):
playerInputI = int(input("Input row: "))
playerInputJ = int(input("Input column: "))
bestIndex = (playerInputI, playerInputJ)
else:
winner, bestIndex = m.minimax(t.grid, (-1, -1), 15, -1)
t.winner = None
t.findWinner(bestIndex, t.grid)
t.updateGameGrid(bestIndex)
print(t.grid)
print(t.grid)
Minimax.py
import Minimax
if (__name__ == "__main__"):
t = TicTacToe.ttt(3)
m = Minimax.Minimax(3, t)
while (t.winner == None):
if (t.turn == 1):
playerInputI = int(input("Input row: "))
playerInputJ = int(input("Input column: "))
bestIndex = (playerInputI, playerInputJ)
else:
winner, bestIndex = m.minimax(t.grid, (-1, -1), 15, -1)
t.winner = None
t.findWinner(bestIndex, t.grid)
t.updateGameGrid(bestIndex)
print(t.grid)
print(t.grid)
Tictactoe.py
from random import randint
class ttt:
def __init__(self, size):
self.gridSize = size
self.grid = self.createGrid()
# If using minimax algorithm, user is maximizer(1) and computer is minimizer(-1)
# If single player, then user is 1, computer is -1
# If multiplayer, user1 is 1, user2 = -1
self.turn = 1
self.winner = None
def createGrid(self):
grid = []
for i in range(self.gridSize):
grid.append([])
for j in range(self.gridSize):
grid[i].append(0)
# grid = [[-1, 1, 0], [0, -1, 0], [0, 0, 0]]
return grid
def updateGameGrid(self, index):
if (self.grid[index[0]][index[1]] != 0):
return
self.grid[index[0]][index[1]] = self.turn
winner = self.findWinner(index, self.grid)
self.turn = -self.turn
def randomIndex(self):
x = randint(0, self.gridSize-1)
y = randint(0, self.gridSize-1)
while (self.grid[x][y] != 0):
x = randint(0, self.gridSize-1)
y = randint(0, self.gridSize-1)
return (x, y)
def findWinner(self, index, grid):
# Row
found = True
for j in range(self.gridSize-1):
if (grid[index[0]][j] != grid[index[0]][j+1] or grid[index[0]][j] == 0):
found = False
break
if (found):
self.winner = self.turn
return self.turn
# Column
found = True
for i in range(self.gridSize-1):
if (grid[i][index[1]] != grid[i+1][index[1]] or grid[i][index[1]] == 0):
found = False
break
if (found):
self.winner = self.turn
return self.turn
# Top Left to Bottom Right Diagonal
if (index[0] == index[1]):
found = True
for i in range(self.gridSize-1):
if (grid[i][i] != grid[i+1][i+1] or grid[i][i] == 0):
found = False
break
if (found):
self.winner = self.turn
return self.turn
# Top Right to Bottom Left Diagonal
if (index[0] + index[1] == self.gridSize-1):
found = True
for i in range(self.gridSize-1):
if (grid[self.gridSize-i-1][i] != grid[self.gridSize-i-2][i+1] or grid[self.gridSize-i-1][i] == 0):
found = False
break
if (found):
self.winner = self.turn
return self.turn
tie = True
for i in range(self.gridSize):
for j in range(self.gridSize):
if (grid[i][j] == 0):
tie = False
if (tie):
self.winner = 0
return 0
return None
The grid is represented as a 2d array, with each element being either a -1 for O, 1 for X and 0 for nothing. The player is 1 and the computer is -1. Who's turn it is is represented as a -1 or 1, corresponding to O or X. If anyone is able to find where the error in my code is that would be a great.
I'm trying to solve the 15-Puzzle problem using IDA* algorithm and Manhattan heuristic.
I already implemented the algorithm from the pseudocode in this Wikipedia page (link).
Here's my code so far :
def IDA(initial_state, goal_state):
initial_node = Node(initial_state)
goal_node = Node(goal_state)
threshold = manhattan_heuristic(initial_state, goal_state)
path = [initial_node]
while 1:
tmp = search(path, goal_state, 0, threshold)
if tmp == True:
return path, threshold
elif tmp == float('inf'):
return False
else:
threshold = tmp
def search(path, goal_state, g, threshold):
node = path[-1]
f = g + manhattan_heuristic(node.state, goal_state)
if f > threshold:
return f
if np.array_equal(node.state, goal_state):
return True
minimum = float('inf')
for n in node.nextnodes():
if n not in path:
path.append(n)
tmp = search(path, goal_state, g + 1, threshold)
if tmp == True:
return True
if tmp < minimum:
minimum = tmp
path.pop()
return minimum
def manhattan_heuristic(state1, state2):
size = range(1, len(state1) ** 2)
distances = [count_distance(num, state1, state2) for num in size]
return sum(distances)
def count_distance(number, state1, state2):
position1 = np.where(state1 == number)
position2 = np.where(state2 == number)
return manhattan_distance(position1, position2)
def manhattan_distance(a, b):
return abs(b[0] - a[0]) + abs(b[1] - a[1])
class Node():
def __init__(self, state):
self.state = state
def nextnodes(self):
zero = np.where(self.state == 0)
y,x = zero
y = int(y)
x = int(x)
up = (y - 1, x)
down = (y + 1, x)
right = (y, x + 1)
left = (y, x - 1)
arr = []
for direction in (up, down, right, left):
if len(self.state) - 1 >= direction[0] >= 0 and len(self.state) - 1 >= direction[1] >= 0:
tmp = np.copy(self.state)
tmp[direction[0], direction[1]], tmp[zero] = tmp[zero], tmp[direction[0], direction[1]]
arr.append(Node(tmp))
return arr
I'm testing this code with a 3x3 Puzzle and here's the infinite loop! Due to the recursion I have some trouble testing my code...
I think the error might be here : tmp = search(path, goal_state, g + 1, threshold) (in the search function). I'm adding only one to the g cost value. It should be correct though, because I can only move a tile 1 place away.
Here's how to call the IDA() function:
initial_state = np.array([8 7 3],[4 1 2],[0 5 6])
goal_state = np.array([1 2 3],[8 0 4],[7 6 5])
IDA(initial_state, goal_state)
Can someone help me on this ?
There are couple of issues in your IDA* implementation. First, what is the purpose of the variable path? I found two purposes of path in your code:
Use as a flag/map to keep the board-states that is already been visited.
Use as a stack to manage recursion states.
But, it is not possible to do both of them by using a single data structure. So, the first modification that your code requires:
Fix-1: Pass current node as a parameter to the search method.
Fix-2: flag should be a data structure that can perform a not in query efficiently.
Now, fix-1 is easy as we can just pass the current visiting node as the parameter in the search method. For fix-2, we need to change the type of flag from list to set as:
list's average case complexity for x in s is: O(n)
set's
Average case complexity for x in s is: O(1)
Worst case complexity for x in s is: O(n)
You can check more details about performance for testing memberships: list vs sets for more details.
Now, to keep the Node information into a set, you need to implement __eq__ and __hash__ in your Node class. In the following, I have attached the modified code.
import timeit
import numpy as np
def IDA(initial_state, goal_state):
initial_node = Node(initial_state)
goal_node = Node(goal_state)
threshold = manhattan_heuristic(initial_state, goal_state)
#print("heuristic threshold: {}".format(threshold))
loop_counter = 0
while 1:
path = set([initial_node])
tmp = search(initial_node, goal_state, 0, threshold, path)
#print("tmp: {}".format(tmp))
if tmp == True:
return True, threshold
elif tmp == float('inf'):
return False, float('inf')
else:
threshold = tmp
def search(node, goal_state, g, threshold, path):
#print("node-state: {}".format(node.state))
f = g + manhattan_heuristic(node.state, goal_state)
if f > threshold:
return f
if np.array_equal(node.state, goal_state):
return True
minimum = float('inf')
for n in node.nextnodes():
if n not in path:
path.add(n)
tmp = search(n, goal_state, g + 1, threshold, path)
if tmp == True:
return True
if tmp < minimum:
minimum = tmp
return minimum
def manhattan_heuristic(state1, state2):
size = range(1, len(state1) ** 2)
distances = [count_distance(num, state1, state2) for num in size]
return sum(distances)
def count_distance(number, state1, state2):
position1 = np.where(state1 == number)
position2 = np.where(state2 == number)
return manhattan_distance(position1, position2)
def manhattan_distance(a, b):
return abs(b[0] - a[0]) + abs(b[1] - a[1])
class Node():
def __init__(self, state):
self.state = state
def __repr__(self):
return np.array_str(self.state.flatten())
def __hash__(self):
return hash(self.__repr__())
def __eq__(self, other):
return self.__hash__() == other.__hash__()
def nextnodes(self):
zero = np.where(self.state == 0)
y,x = zero
y = int(y)
x = int(x)
up = (y - 1, x)
down = (y + 1, x)
right = (y, x + 1)
left = (y, x - 1)
arr = []
for direction in (up, down, right, left):
if len(self.state) - 1 >= direction[0] >= 0 and len(self.state) - 1 >= direction[1] >= 0:
tmp = np.copy(self.state)
tmp[direction[0], direction[1]], tmp[zero] = tmp[zero], tmp[direction[0], direction[1]]
arr.append(Node(tmp))
return arr
initial_state = np.array([[8, 7, 3],[4, 1, 2],[0, 5, 6]])
goal_state = np.array([[1, 2, 3],[8, 0, 4],[7, 6, 5]])
start = timeit.default_timer()
is_found, th = IDA(initial_state, goal_state)
stop = timeit.default_timer()
print('Time: {} seconds'.format(stop - start))
if is_found is True:
print("Solution found with heuristic-upperbound: {}".format(th))
else:
print("Solution not found!")
Node: Please double check your Node.nextnodes() and manhattan_heuristic() methods as I did not pay much attention in those areas. You can check this GitHub repository for other algorithmic implementations (i.e., A*, IDS, DLS) to solve this problem.
References:
Python Wiki: Time Complexity
TechnoBeans: Performance for testing memberships: list vs tuples vs sets
GitHub: Puzzle Solver (by using problem solving techniques)
Hello i am trying to learn some basics by making some mistakes.
I am making a basic python based snake game, using as little of non built in modules as i can.
at the moment the main one is tkinter.
Some of my logic is changing a member of a list, when i do not expect it do so and i can't see why.
The change is happening at line 60 (21) where index 1 is changed to equal index 0
when only index 0 should change only when "middle" occurs on line 71 (31)
the error is within this function
self.player[1] changes when i did not expect
def move(self):
print("old snake pos {}".format(self.player))
for i in range(len(self.player)-1, -1, -1):
# runs through loop backwards as the positions of the next piece needs to be known
print(i)
if i == len(self.player)-1:
print("last piece")
if self.eat():
print("eat = True")
self.player.append(self.player[-1])
print("{} changed from {} to {}".format(i, self.player[i], self.player[i-1]))
self.player[i] = self.player[i-1]
else:
if i == 0:
print("Head")
print(self.vel)
print(self.player[0])
print(self.player[1])
self.player[0][0] = self.player[0][0] + self.vel[0]
print("why has it changed????????")
print(self.player[0])
print(self.player[1])
self.player[0][1] = self.player[0][1] + self.vel[1]
print(self.player[1])
print(self.player)
continue
print("middle piece")
print("{} changed from {} to {}".format(i, self.player[i], self.player[i - 1]))
self.player[i] = self.player[i - 1]
print(self.player[i])
print(self.player[i])
print(self.player)
print("new snake pos {}".format(self.player))
i have tried different checks, but it doesnt seem to be doing any line that i dont expect at the right time.
and the full code is this:
# programme for learning more about creating graphical displays in Python
# will start with some investigation and use of tkinter resource
# then move to creating a snake style game
import tkinter as tk
import random as r
import time
class SnakeApp(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.master = master
self.pack()
self.setup()
self.gameloop()
def mat2str(self, matrix):
string = ""
for y in matrix:
for x in y:
string += x
string += "\n"
return string
# random an int co-ordinate
def newpos(self):
return [r.randint(0, self.wx), r.randint(0, self.wy)]
# check if the fruit has been eaten
def eat(self):
if any([s == self.fruit for s in self.player]):
print("fruit col detected {}".format(self.fruit))
# new position
self.fruit = self.newpos()
# increase the game speed by 2.5%
self.dt = self.dt * 0.975
# need to add a segment to the player
return True
# move the game 1 turn and compute all the logic before the next frame is drawn
def move(self):
print("old snake pos {}".format(self.player))
for i in range(len(self.player)-1, -1, -1):
# runs through loop backwards as the positions of the next piece needs to be known
print(i)
if i == len(self.player)-1:
print("last piece")
if self.eat():
print("eat = True")
self.player.append(self.player[-1])
print("{} changed from {} to {}".format(i, self.player[i], self.player[i-1]))
self.player[i] = self.player[i-1]
else:
if i == 0:
print("Head")
print(self.vel)
print(self.player[0])
print(self.player[1])
self.player[0][0] = self.player[0][0] + self.vel[0]
print("why has it changed????????")
print(self.player[0])
print(self.player[1])
self.player[0][1] = self.player[0][1] + self.vel[1]
print(self.player[1])
print(self.player)
continue
print("middle piece")
print("{} changed from {} to {}".format(i, self.player[i], self.player[i - 1]))
self.player[i] = self.player[i - 1]
print(self.player[i])
print(self.player[i])
print(self.player)
print("new snake pos {}".format(self.player))
def up(self, event):
print("up")
if self.vel != [0, 1]:
self.vel = [0, -1]
def down(self, event):
print("down")
if self.vel != [0, -1]:
self.vel = [0, 1]
def left(self, event):
print("left")
if self.vel != [1, 0]:
self.vel = [-1, 0]
def right(self, event):
print("right")
if self.vel != [-1, 0]:
self.vel = [1, 0]
def drawempty(self, wx, wy):
frame = []
for y in range(wy):
xlayer = []
for x in range(wx):
xlayer.append("-")
frame.append(xlayer)
return frame
def redraw(self, player, object):
# self.drawempty(self.wx, self.wy)
# replaced redraw each frame with a static empty frame build
print(self.gameframe)
self.gameframe = self.drawempty(self.wx, self.wy)
print(self.gameframe)
# set the string in the co-ord of the object to A
# check for collision needs to occur before this - all game logic before
# set the string in the co-ords of all players to *
print(object)
self.gameframe[object[1]][object[0]] = "A"
for b in player:
self.gameframe[b[1]][b[0]] = "*"
def setup(self):
# set game size
self.wx = 20
self.wy = 20
self.master.geometry("300x300")
self.vel = [-1, 0]
self.dt = 1
# create text matrices of spaces of size wx * wy
#self.gameframe = tk.Variable()
self.gameframe = []
#self.emptyframe = tk.Variable()
self.emptyframe = self.drawempty(self.wx, self.wy)
self.gameframe = self.emptyframe
# create a player and fruit object in the space
self.player = [[round(self.wx / 2), round(self.wy / 2)],[round(self.wx / 2) + 1, round(self.wy / 2)],[round(self.wx / 2) + 2, round(self.wy / 2)]]
self.fruit = self.newpos()
self.redraw(self.player, self.fruit)
self.game = tk.Text(self, height=self.wy, width=self.wx)
self.game.pack()
self.game.insert(tk.END, self.mat2str(self.gameframe))
self.master.bind("<Up>", self.up)
self.master.bind("<Down>", self.down)
self.master.bind("<Left>", self.left)
self.master.bind("<Right>", self.right)
def gameloop(self):
while True:
self.redraw(self.player, self.fruit)
self.game.delete('1.0', tk.END)
self.game.insert(tk.END, self.mat2str(self.gameframe))
self.move()
self.master.update()
time.sleep(self.dt)
Snake = tk.Tk()
app = SnakeApp(master=Snake)
The problem can be found at your move function as this point (Line 122)
self.player[i] = self.player[i - 1] # i == 1, self.player[1] = self.player[0]
At the point when i is 1 this code block is reached and it sets self.player[1] to self.player[0]
The problem is that self.player[0] after that is equal by reference to self.player[1], which means that when one changes, the other does as well (which you said was your problem).
What you need to do to prevent that from happening is to create a copy of self.player[i-1] and set that equal to self.player[i]. This can be achieved through multiple ways.
In your case because you have a list of lists the following should be fine
self.player[i] = [item for item in self.player[i-1]] # Create a new list
if you want use a built-in functions for this you can use deepcopy
from copy import deepcopy
# ...
self.player[i] = deepcopy(self.player[i-1])
Note that this behavior can also be found on lines 25,43, 104 and 122. So you'll have to change all of them to create a copy as shown above.
The code that should be modified is:
self.player[i] = self.player[i - 1]
When you change element 1 to be element 0, you are not making a copy but making them pointing to the same element, which is 0. So when you change element 0, element 1 change as well.
Example why this leads to what you see in code:
player = [[1,2], [3,4]]
i = 1
player[i] = player[i - 1]
player[0][0] = 100
print(player)
[[100, 2], [100, 2]]
One quick solution to create a copy:
self.player[i] = [x for x in self.player[i - 1]]
How to implement Hanoi sort in python? Rules of Hanoi sort: http://www.dangermouse.net/esoteric/hanoisort.html
My code:
def hanoy_sorted(arr, x, y):
if len(arr) == 1:
print(arr[0], x, y)
elif len(arr) > 1:
hanoy_sorted(arr[1:], x, 6 - x - y)
print(arr[0], x, y)
hanoy_sorted(arr[1:], 6 - x - y, y)
def merge(arr1, x, arr2, y):
if len(arr2) == 0:
hanoy(arr1, x, y)
elif len(arr1) > 0:
n = arr1[-1]
j = len(arr2) - 1
if n < arr2[-1]:
print(arr1[-1], x, y)
j = len(arr2)
else:
for i in range(0, len(arr2)):
if n > arr2[i]:
j = i + 1
break
hanoy_sorted(arr2[j - 1:], y, x)
hanoy_sorted([n] + arr2[j - 1:], x, y)
arr2.insert(j, n)
merge(arr1[:len(arr1) - 1], x, sorted(arr2)[::-1], y)
def hanoy(arr, x, y):
if len(arr) == 1:
print(arr[0], x, y)
if len(arr) > 1:
mi = arr.index(max(arr))
up = arr[mi + 1:]
z.append(up[:])
hanoy(z[-1], x, 6 - x - y)
print(arr[mi], x, y)
merge(arr[:mi], x, sorted(up)[::-1], 6 - x - y)
arr.remove(arr[mi])
hanoy_sorted(sorted(arr)[::-1], 6 - x - y, y)
del z[-1]
n = int(input())
arr = list(map(int, input().split()))[::-1]
z = []
hanoy(arr, 1, 3)
There is an error in function merge: It sometimes puts bigger disk on smaller. How to fix that?
This programme prints what disk to move from rod1, to rod2.
Updated:
So I rewrote all my code and now I have that:
from sys import setrecursionlimit
setrecursionlimit(10000000)
def gen_moves(pos, prev_move):
moves = []
not_to_go = [prev_move[0], prev_move[2], prev_move[1]]
not_to_go2 = [prev_move[0], prev_move[2], 6 - prev_move[2] - prev_move[1]]
first, second, third = pos
if first != []:
if second != []:
if second[-1] > first[-1]:
moves.append([first[-1], 1, 2])
else:
moves.append([second[-1], 2, 1])
else:
moves.append([first[-1], 1, 2])
if third != []:
if third[-1] > first[-1]:
moves.append([first[-1], 1, 3])
else:
moves.append([third[-1], 3, 1])
else:
moves.append([first[-1], 1, 3])
else:
if second != []:
moves.append([second[-1], 2, 1])
if third != []:
moves.append([third[-1], 3, 1])
if second != []:
if third != []:
if second[-1] > third[-1]:
moves.append([third[-1], 3, 2])
else:
moves.append([second[-1], 2, 3])
else:
moves.append([second[-1], 2, 3])
else:
if third != []:
moves.append([third[-1], 3, 2])
if not_to_go in moves:
moves.remove(not_to_go)
if not_to_go2 in moves:
moves.remove(not_to_go2)
return moves
def do_move(arr, move):
pos = arr[:]
disk, from_rod, to_rod = move
pos[from_rod - 1] = pos[from_rod - 1][:-1]
pos[to_rod - 1] = pos[to_rod - 1] + [disk]
return pos
def hanoi(arr, solved, prev_move):
if not solved:
moves = gen_moves(arr, prev_move)
for move in moves:
pos = arr[:]
pos = do_move(pos, move)
sorted_arr = sorted(arr[0] + arr[1] + arr[2])[::-1]
for i in range(3):
if pos[i] == sorted_arr:
solved = True
solved = solved or hanoi(pos, solved, move)
if solved:
moves_to_solve.append(move)
break
return solved
n = int(input())
arr = [list(map(int, input().split()))[::-1], [], []]
solved = False
moves_to_solve = []
sorted_arr = sorted(arr[0] + arr[1] + arr[2])[::-1]
for i in range(3):
if arr[i] == sorted_arr:
solved = True
if n > 1 and not solved:
solved = hanoi(arr, solved, [0, 0, 0])
moves_to_solve = moves_to_solve[::-1]
for move in moves_to_solve:
print(*move)
It works right but if there are eleven disks programme prints segmentation fault. How to fix that?
You can simply use a recursive approach with a helper function to implement the tower of hanoi. Something like this;
def moveTower(height,fromPole, toPole, withPole):
if height >= 1:
moveTower(height-1,fromPole,withPole,toPole)
moveDisk(fromPole,toPole)
moveTower(height-1,withPole,toPole,fromPole)
def moveDisk(fp,tp):
print("moving disk from",fp,"to",tp)