I have a class that has names as strings and you can change its names (you can see the class below)
but when i change the names of the Squares with the write function it does something weird:
trying to update a new array it changes both the old and the new array
I will also include the write_word function below as code
Update:I also tried to add the .copy to the write_word function, Ill add the new version below
Update2: I also tried to create the boards as a list, and write words into the list. I added both the array and the list versions of both create and write functions. In both of them the problem is the same, once I create a board and copy it and change the copy, both the original and the copy changes. This leads me to believe that the problem is caused by the class object and not the arrays.
Update3: it seems like both the list and numpy have the same problem, I will now try to do the same thing without using class at all. I will update whether it works or not
Update4: I didnt have to change the class at all and now my code does what it had to do thanks to the answer. I will still add the code that DOES work under the FINAL_write_word function.
class Square:
def __init__(self, x,y):
self.x = x
self.y = y
self.letter = " "
self.num = 0
def write(self, letter):
if self.letter != "!":
self.letter = letter.upper()
else:
print("cannot write on black square")
def clear(self):
if self.letter != "!":
self.letter = " "
else:
print("cannot write on black square")
def black(self):
self.letter = "!"
def isblack(self):
if self.letter == "!":
return True
else:
return False
def isfilled(self):
if self.letter != "!" and self.letter != " ":
return True
else:
return False
def read(self):
return self.letter
def number(self,num):
self.num = num
def getnum(self):
return self.num
def __str__(self):
image = " " + self.letter
return image
def __repr__(self):
image = " " + self.letter
return image
def write_word(crossarray,indexes,word):
print("now writing word:",word)
temparray = crossarray
for i, index in enumerate(indexes):
row = int(index // 5)
col = int(index % 5)
temparray[row,col].write(word[i])
return temparray
def UPDATED_write_word(crossarray,indexes,word):
print("now writing word:",word)
temparray = crossarray.copy()
for i, index in enumerate(indexes):
row = int(index // 5)
col = int(index % 5)
temparray[row,col].write(word[i])
return temparray
def create_crossword(down,across,indexes,blacks):
cross_array = np.zeros((5,5), dtype=Square)
for row in range(len(cross_array)):
for col in range(len(cross_array[0])):
cross_array[row,col] = Square(row,col)
for name, index in enumerate(indexes):
row = int(index // 5)
col = int(index % 5)
cross_array[row,col].number(name+1)
for index in blacks:
row = int(index // 5)
col = int(index % 5)
cross_array[row,col].black()
return(cross_array)
def create_crosslist(down,across,indexes,blacks):
cross_list = []
for row in range(5):
rowlist = []
for col in range(5):
rowlist.append(Square(row,col))
cross_list.append(rowlist)
for name, index in enumerate(indexes):
row = int(index // 5)
col = int(index % 5)
cross_list[row][col].number(name+1)
for index in blacks:
row = int(index // 5)
col = int(index % 5)
cross_list[row][col].black()
return cross_list
def write_word_list(crosslist,indexes,word):
print("now writing word:",word)
templist = crosslist
for i, index in enumerate(indexes):
row = int(index // 5)
col = int(index % 5)
templist[row][col].write(word[i])
return templist
import copy
def FINAL_write_word(crossarray,indexes,word):
print("now writing word:",word)
temparray = copy.deepcopy(crossarray)
for i, index in enumerate(indexes):
row = int(index // 5)
col = int(index % 5)
temparray[row,col].write(word[i])
return temparray
I hope this helps you:
old = np.arange(5)
new = old
new[0] = -1
The code above gives:
old
#array([-1, 1, 2, 3, 4])
new
#array([-1, 1, 2, 3, 4])
To avoid this you have to make a copy:
old = np.arange(5)
new = old.copy()
new[0] = -1
Which gives you:
old
#array([0, 1, 2, 3, 4])
new
#array([-1, 1, 2, 3, 4])
EDIT
In your particular case you have to:
import copy
and change the line:
temparray = crossarray
by:
temparray = copy.deepcopy(crossarray)
So I have this piece of code I'm trying to make generate level layouts for a grid of rooms. The first time through the mainloop it runs perfectly it runs and does exactly what it should but the second time through it pauses just after the first print error and gives me the attached error and I cant figure out what's wrong with it.
(the y/n prompt is only to slow down the program so I can see what's happening)
userInput = ""
roomChance = 0.5
world = [[0,0,0], \
[0,1,0], \
[0,0,0]]
possibleWorld = []
newX = []
def check_neighbours(xy):
possibleWorld.clear()
yLoops = 0
for y in xy:
print(" y:", y)
xLoops = 0
for x in y:
print(" x:", x)
#Check left cell
if(xLoops-1 >= 0):
if(y[xLoops-1] == 1):
possibleWorld.append([xLoops, yLoops])
print("x-1:", y[xLoops-1])
#Check right cell
if(xLoops+1 < len(y)):
if(y[xLoops+1] == 1):
possibleWorld.append([xLoops, yLoops])
print("x+1:", y[xLoops+1])
#Check above cell
if(yLoops-1 >= 0):
if(xy[yLoops-1][xLoops] == 1):
possibleWorld.append([xLoops, yLoops])
print("y-1:", xy[yLoops-1][xLoops])
#Check above cell
if(yLoops+1 < len(xy)):
if(xy[yLoops+1][xLoops] == 1):
possibleWorld.append([xLoops, yLoops])
print("y+1:", xy[yLoops+1][xLoops])
print("\n")
xLoops += 1
yLoops += 1
def assign_neighbours(possible, world, chance):
for i in possible:
if(random.random() < chance):
world[i[1]][i[0]] = 1
possible.clear()
def border_expand(world):
for x in world[0]:
if(x == 1):
for i in world[0]:
newX.append(0)
world.insert(0, newX)
newX.clear
break
def print_world(world):
for y in world:
print(y)
# ==================== Mainloop ====================
while(True):
userInput = input(print("Generate Level? Y/N?"))
check_neighbours(world)
print(possibleWorld)
assign_neighbours(possibleWorld, world, roomChance)
print_world(world)
border_expand(world)
print("\n")
print_world(world)
File "C:\Users\Potato\Desktop\Level gen_query.py", line 96, in <module>
border_expand(world)
File "C:\Users\Potato\Desktop\Level gen_query.py", line 67, in border_expand
newX.append(0)
MemoryError```
You're not calling newX.clear, so it is continually growing. When you run world.insert(0, newX) you are inserting a reference to newX into world[0], even if you did call newX.clear() you would not get the behaviour you want as the first element in world would be empty.
You need to create a new list on every call to border_expand so that it is a new list every time
def border_expand(world):
newX = []
for x in world[0]:
if(x == 1):
for i in world[0]:
newX.append(0)
world.insert(0, newX)
break
I'm making a scalable tic tac toe game in Tkinter (meaning the board size can be 2x2 up to whatever fits the screen). I'm using cget("image") to find what mark a button has. For some reason, the win check displays very random things. I've tried a lot of semi-random things to fix it, but had no success in fixing it. Here's the code:
from tkinter import *
class XOGame:
def main_game(self):
self.__game_window = Tk()
self.__grid_size = 3 # User inputted in a different part of the code
self.__game_window.title("Tic Tac Toe (" + str(self.__grid_size) + "x"
+ str(self.__grid_size) + ")")
# this is user inputted in a different part of the program.
self.__players = ["p1", "p2"]
self.__player1 = self.__players[0]
self.__player2 = self.__players[1]
self.build_board(self.__game_window)
self.__game_window.mainloop()
def build_board(self, window):
self.__size = self.__grid_size ** 2
self.__turn_nr = 1
self.__win = False
self.__empty_square = PhotoImage(master=window,
file="rsz_empty.gif")
self.__x = PhotoImage(master=window,
file="rsz_cross.gif")
self.__o = PhotoImage(master=window,
file="rsz_nought.gif")
self.__squares = [None] * self.__size
self.create_win_check_lists()
# Building the buttons and gridding them
for i in range(self.__size):
self.__squares[i] = (Button(window, image=self.__empty_square))
row = 0
column = 0
number = 1
for j in self.__squares:
j.grid(row=row, column=column)
j.config(command=lambda index=self.__squares.index(j):
self.change_mark(index))
column += 1
if number % 3 == 0:
row += 1
column = 0
number += 1
# This is the part where the picture changing happens.
def change_mark(self, i):
"""
Function changes mark of empty button to either X or O depending on the
player in turn. It also checks if the change in mark results in a win.
:param i: The button number, whose mark is being changed
:return: None
"""
if self.__turn_nr % 2 == 1:
self.__player_in_turn = self.__player1
else:
self.__player_in_turn = self.__player2
if self.__player_in_turn == self.__player1:
self.__mark = self.__x
else:
self.__mark = self.__o
self.__squares[i].configure(image=self.__mark, state=DISABLED)
self.__turn_nr += 1
self.check_win(i)
if self.__win is True:
print("this is thought to be a win")
else:
print("the game thinks this is not a win")
# Checking if the game tied.
if self.__turn_nr == self.__size + 1 and not self.__win:
print("the game thinks it tied.")
def check_win(self, i):
"""
Checks if mark placement leads to a win.
:param i: i is the button location.
:return: None
"""
# checks row
if self.__win == False:
for row in self.__rows:
if i + 1 in row:
self.__win = self.checksameimage(row)
if self.__win == True:
break
# checks column
if self.__win == False:
for column in self.__columns:
if i + 1 in column:
self.__win = self.checksameimage(column)
if self.__win == True:
break
# if i is in a diagonal, checks one/both diagonals
if self.__win == False:
for diag in self.__diagonals:
if i + 1 in diag:
self.__win = self.checksameimage(diag)
if self.__win == True:
break
return self.__win
# checking if all the images are same
# This is likely where the issue is. Either this part or checkEqual.
def checksameimage(self, lst):
images = []
for nr in lst:
try:
images.append(self.__squares[nr].cget("image"))
except IndexError:
pass
return self.checkEqual(images)
def checkEqual(self, lst):
"""
Function checks if all elements in a list are equal. Used for checking
if the dice throws are the same.
:param lst: The list the check is performed on
:return: True/False, True if all elements are equal.
"""
if all(x == lst[0] for x in lst):
return True
else:
return False
def create_win_check_lists(self):
"""
Creates lists whose elements are lists of the locations of each spot
of the game board that belongs to a row/column/diagonal
:return:
"""
self.__rows = [[] for _ in range(self.__grid_size)]
for i in range(self.__grid_size):
self.__rows[i].append(i + 1)
for k in range(1, self.__grid_size):
self.__rows[i].append(i + 1 + self.__grid_size * k)
self.__columns = [[] for _ in range(self.__grid_size)]
for i in range(self.__grid_size):
for j in range(1, self.__grid_size + 1):
self.__columns[i].append(i * self.__grid_size + j)
self.getDiagonals(self.__columns)
def getDiagonals(self, lst):
self.__diagonals = [[], []]
self.__diagonals[0] = [lst[i][i] for i in range(len(lst))]
self.__diagonals[1] = [lst[i][len(lst) - i - 1] for i in
range(len(lst))]
def start(self):
# Function starts the first window of the game.
self.main_game()
def main():
ui = XOGame()
ui.start()
main()
The images used in the code are 125x125. Here is a link that works for 24h: https://picresize.com/b5df006025f0d8
your problem is unquestionably with indices. We can see what index each square corresponds to by changing the image to be text corresponding to the index:
Try changing this loop in build_board:
for i in range(self.__size):
self.__squares[i] = (Button(window, image=self.__empty_square))
To instead show the index it corresponds to:
for i in range(self.__size):
self.__squares[i] = (Button(window, text=str(i)))
Then print out lst passed to checksameimage and you will see that the indices you are checking are all one higher than you intended.
I'm trying to code an exercise to solve the Queen Puzzle (yes, typical, I know) on Python. I've made a class called Queens for board state that takes in the length of each side and the list of queens, if any.
In the main program, I have a list of Queens called frontier, which is then popped one by one. However, the result I get from popping seems to be of type list, and not Queens as expected!
What is causing this, and how do I fix it?
Code snippet:
from queens import Queens
def search(n, debug=False, max=6):
frontier = [Queens(n, [])] # queue of states to explore
while frontier != []:
check = frontier.pop()
print(type(check))
if debug:
print(str(numSolutions) + " | Checking:")
print(check)
v = check.validate()
# EDIT: added more of the code; problem seems to arise here
if v == 0:
if debug:
print("Solution!")
numSolutions += 1
if n <= max:
solutions.append(check)
elif v > 0:
if debug:
print(str(v) + " more")
frontier.append(check.branch())
else:
if debug:
print("Invalid state")
pass
expected output:
<class 'queens.Queens'>
actual output:
<class 'queens.Queens'>
<class 'list'>
(yes, the one type statement printed 2 lines)
EDIT: Since there seems to be no problem with the main code, here's the file in which I defined the class:
import array
import copy
class Queens:
__slots__ = ["n", "qlist"]
def __init__(self, n, qa=None):
self.n = n # store for print function
if qa == None:
self.qlist = array.array("h")
elif type(qa) == list:
self.qlist = array.array("h", qa)
else:
assert type(qa) == array.array
self.qlist = qa # list of positions for each line
def __str__(self):
out = ""
for q in range(self.n):
if q == 0:
out += "|"
else:
out += "\n|"
for space in range(self.n):
if q < len(self.qlist) and space == self.qlist[q]:
out += "Q|"
else:
out += " |"
return out
def branch(self):
out = []
for x in range(self.n):
if x not in self.qlist:
qlist = copy.deepcopy(self.qlist)
qlist.append(x)
out.append(Queens(self.n, qlist))
return out
def validate(self):
for y in range(len(self.qlist)):
# don't need to check horizontal;
# data structure doesn't let you place them
# don't need to check vertical;
# branching eliminates those
# check diagonals
for y2 in range(len(self.qlist)):
if y != y2:
expected = self.qlist[y] - y + y2
if 0 <= expected < self.n and self.qlist[y2] == expected:
return -1
expected = self.qlist[y] + y - y2
if 0 <= expected < self.n and self.qlist[y2] == expected:
return -1
return self.n - len(self.qlist)
if __name__ == "__main__":
q = Queens(4)
print(q.validate())
q = Queens(4, [0, 1, 2])
print(q.validate())
I've figured it out. The problem happened only after frontier.append(check.branch()). branch() returns a list of queens. I thought I was appending several queens to frontier, but I was, in fact, appending a list of queens to frontier. Changing append to extend solved the issue.
When you append to your frontier the result of .branch(..) and you re-iterate you get an array back (list). Which is being printed after the loop continues to the next step.
def branch(self):
out = []
for x in range(self.n):
if x not in self.qlist:
qlist = copy.deepcopy(self.qlist)
qlist.append(x)
out.append(Queens(self.n, qlist))
return out
I'm making a sudoku board using turtle in python. Right now I'm having a little trouble trying to figure out how to let the program easily check which box I am looking at. I have 5 grids in different positions. The game will automatically pick one to play. Since each grid is in a different position, I have to be careful where I have my turtle positioned. I have 16 functions that calculate where I want my turtle cursor to be to draw the number.
#Below is a guide to where each box is relative to its position on the grid.
#To find center of the box, add 75 to x and 20 to y.
#Box(1,1) = x, y Box(1,2) = x+150, y Box(1,3) = x+300, y Box(1,4) = x+450, y
#Box(2,1) = x, y-150 Box(2,2) = x+150, y-150 Box(2,3) = x+300, y-150 Box(2,4) = x+450, y-150
#Box(3,1) = x, y-300 Box(3,2) = x+150, y-300 Box(3,3) = x+300, y-300 Box(3,4) = x+450, y-300
#Box(4,1) = x, y-450 Box(4,2) = x+150, y-450 Box(4,3) = x+300, y-450 Box(4,4) = x+450, y-450
def box1_1(x, y):
return(x+75, y+20)
def box1_2(x, y):
return(x+225, y+20)
def box1_3(x, y):
return(x+375, y+20)
def box1_4(x, y):
return(x+525, y+20)
def box2_1(x, y):
return(x+75, y-130)
def box2_2(x, y):
return(x+225, y-130)
def box2_3(x, y):
return(x+375, y-130)
def box2_4(x, y):
return(x+525, y-130)
def box3_1(x, y):
return(x+75, y-280)
def box3_2(x, y):
return(x+225, y-280)
def box3_3(x, y):
return(x+375, y-280)
def box3_4(x, y):
return(x+525, y-280)
def box4_1(x, y):
return(x+75, y-430)
def box4_2(x, y):
return(x+225, y-430)
def box4_3(x, y):
return(x+375, y-430)
def box4_4(x, y):
return((x+525, y-430))
I can get the boards to be drawn how I want and the given numbers are also drawn correctly. I want the user to have the option to play with real time correction or play traditionally where they can enter all the values and the game will check if their solution is correct. The problem I am running into is during the part where the user enters in their desired number into a box. Right now I have:
def playSudoku():
playboard = []
board1 = [[0,0,0,3],[0,1,0,4],[4,2,0,1],[0,3,4,0]]
board2 = [[4,3,0,0],[1,0,3,0],[0,0,2,0],[2,1,0,0]]
board3 = [[0,4,0,1],[3,0,3,0],[1,0,0,4],[0,2,1,0]]
board4 = [[1,0,3,0],[0,4,2,1],[0,0,0,2],[0,0,4,0]]
board5 = [[0,4,3,2],[3,0,0,0],[4,0,0,0],[0,0,4,1]]
boardchoice = random.randint(1,5)
if boardchoice == 1:
drawBoard1()
playboard = board1
posx = -900
posy = 850
elif boardchoice == 2:
drawBoard2()
playboard = board2
posx = -200
posy = 850
elif boardchoice == 3:
drawBoard3()
playboard = board3
posx = 500
posy = 850
elif boardchoice == 4:
drawBoard4()
playboard = board4
posx = -500
posy = 100
else:
drawBoard5()
playboard = board5
posx = 200
posy = 100
rtc = turtle.textinput("Sudoku", "Do you want to play with real time correction?")
if rtc == 'y':
for i in range (0,3):
for j in range (0,3):
if playboard[i][j] != 0:
pass
num = turtle.numinput('Sudoku', 'Enter a number for box[' + str(i) + ',' + str(j) + ']?: ', minval=1, maxval=4)
turtle.goto(box[i]_[j](posx,posy))
turtle.write(str(num), move=True, align="center", font=("Times New Roman", 24))
I want to bring attention specifically to this line
turtle.goto(box[i]_[j](posx,posy))
The reason why each board has a different posx and posy is because they sit in different spots on the turtle grid. So as you saw above, I have functions with names "boxA _ B" where A and B are the position numbers for the box. I know that what I wrote doesn't work (I tried it too, just in case), but I was wondering if there was a python function out there that would let me do what I'm trying to achieve.
In my loop, whatever i and j are, I would like my my program to use box[i]_[j].
I apologize if this is poorly worded. If more clarification is needed, I will eagerly tell you. I was just hoping someone out there knows what I'm looking for.
You should probably define the box function like this.
def box(i, j, x, y):
return (x + 150*j - 75, y - 150*i + 170)
Then just call box(i, j, posx, posy) instead.
Although in this particular case writing a generic box() function as shown in the other answer would be the best approach since the function's result can be determined via a relatively simply formula from the i and j arguments. However in more general circumstances where that's not true, you'd want to use something like a dictionary to map the different possible combinations to the desired function. Below is how that could have been to solve your problem.
First add a dictionary to associate the different argument values with the corresponding function:
box_map = {
(1, 1): box1_1,
(1, 2): box1_2,
(1, 3): box1_3,
(1, 4): box1_4,
(2, 1): box2_1,
(2, 2): box2_2,
(2, 3): box2_3,
(2, 4): box2_4,
(3, 1): box3_1,
(3, 2): box3_2,
(3, 3): box3_3,
(3, 4): box3_4,
(4, 1): box4_1,
(4, 2): box4_2,
(4, 3): box4_3,
(4, 4): box4_4,
}
Next would be to modify the playSudoku() function to use it to lookup the function to call:
...
rtc = turtle.textinput("Sudoku", "Do you want to play with real time correction?")
if rtc == 'y':
for i in range (0,3):
for j in range (0,3):
if playboard[i][j] != 0:
pass
num = turtle.numinput('Sudoku', 'Enter a number for box[' + str(i) + ',' + str(j) + ']?: ', minval=1, maxval=4)
# turtle.goto(box[i]_[j](posx, posy))
turtle.goto(box_map[i, j](posx, posy))
turtle.write(str(num), move=True, align="center", font=("Times New Roman", 24))