I am currently working on a project for one of my classes where I have to implement an AI opponent to play tic-tac-toe using minmax and Alpha-Beta minmax algorithms to determine the moves.
The problem I am having however is attempting to generate a list of possible moves for a board.
My problem code is as follows
def genMoves(genBoard, turnNumber):
moveList = []
print "inMovesList"
#Figure out if X or O go now
if turnNumber % 2 == 0:
moveChar = "O"
else:
moveChar = "X"
i = 0;
while i < 9:
tempBoard = genBoard
if tempBoard[i] == "*":
#set tempBoard[i] to X or O
tempBoard[i] = moveChar
#append move, new board
moveList.append((i, tempBoard))
i+=1
print "MovesList: "
print moveList
return moveList
My board is represented as a list of 9 strings initialized to ["*", "*", "*", "*", "*", "*", "*", "*", "*"].
My goal is to have move list return a list of tuples with the first element of the tuple being i (Where the X or O was inserted) and the second element being the resulting board.
The problem I have is that I will recieve a list with the correct number of possible moves (Eg: If I manually play the first 4 moves for both sides it will only give me 5 possible moves) however it will place the same move in each location that contains a *. (So it ends up generating something like X,O,O,O,O,O,O,O,O for possible second moves)
This is not the first time I have had to use minmax but it is the first time I've had to do it in python.
Any suggestions for how to get around this issue would be helpful!
Thanks!
This line is a problem:
tempBoard = genBoard
After this line, you seem to think that you have two lists -- the original, still referenced
by genBoard, and a new one, now referenced by tempBoard. This is not the case.
This line does not create a copy of the list. Instead, it binds the name tempBoard to refer to the same object that genBoard is bound to.
Consequently, subsequent references to tempBoard[i] also affect genBoard[i].
Try one of these instead:
tempBoard = list(genBoard)
tempBoard = genBoard[:]
tempBoard = copy.copy(genBoard)
Each of these lines creates a new list, the initial contents of which is the same as genBoard. tempboard is bound to this new list, while genboard remains bound to the old list.
If the object in question were more complicated than a list of strings, you might need to do this:
tempBoard = copy.deepcopy(genBoard)
I belive Python does not create a copy of your Board but just points to the original version. therefore your printout is:
"MovesList: "
[[0,(X,O,O,O,O,O,O,O,O)],[1,(X,O,O,O,O,O,O,O,O)],[2,(X,O,O,O,O,O,O,O,O)], etc.
and your genBoard, variable is changed.
to test that add
print genBoard
directly before the end of your method
if that was indeed the problem try to google how to create a copy of your Board instead of referencing to it.
Related
I'm a newbie programmer working on an idea for a small game. I wanted my play space to be a grid for various reasons. Without a lot of good reason, I decided to create a class of GridSquare objects, each object having properties like size, an index to describe what (x,y) coordinates they represented, and some flags to determine if the grid squares were on land or empty space, for example. My grid is a dictionary of these objects, where each GridSquare is a key. The values in the dictionary are going to be various objects in the place space, so that I can easily look up which objects are on each grid square.
Just describing this I feel like a complete lunatic. Please bear in mind that I've only been at this a week.
My problem appears when I try to change the GridSquare objects. For example, I want to use a list to generate the land on each level. So I iterate over the list, and for each value I look through my grid squares using a for loop until I find one with the right index, and flip the GridSquare.land property. But I found that this caused a runtime error, since I was changing keys in a dictionary I was looping through. OK.
Now what I'm trying to do is to create a list of the keys I want to change. For each item in my level-generating list, I go through all the GridSquares in my grid dictionary until I find the one with the index I'm looking for, then I append that GridSquare to a list of old GridSquares that need updating. I then make another copy of the GridSquare, with some properties changed, in a list of altered GridSquares. Finally, I delete any keys from my grid dictionary which match my list of "old" GridSquares, and then add all of the altered ones into my grid dictionary.
The problem is that when I delete keys from my grid dictionary which match my list of "old" keys, I run into keyerrors. I can't understand what is happening to my keys before I can delete them. Using try/except, I can see that it's only a small number of the keys, which seems to vary kind of arbitrarily when I change parts of my code.
I would appreciate any insight into this behaviour.
Here is code for anyone still reading:
aspect_ratio = (4, 3)
screen_size = (1280, 720)
#defining a class of objects called GridSquares
class GridSquare:
def __init__(self, x, y):
self.index = (x, y)
self.land = 0
#creates a dictionary of grid squares which I hope will function as a grid......
grid = {}
for x_index in range(1, (aspect_ratio[0] + 1)):
for y_index in range (1, (aspect_ratio[1] + 1)):
new_square = GridSquare(x_index, y_index)
grid[new_square] = None
#these are lists to hold changes I need to make to the dictionary of grid squares
grid_changes = []
old_gridsquares = []
#this unweildly list is meant to be used to generate a level. Numbers represent land, spaces are empty space.
for number_no, number in enumerate(["1", "1", "1", "1",
" ", " ", " ", " ",
"1", "1", "1", "1"]):
#makes grid squares land if they are designated as such in the list
for gridsquare in grid.keys():
#this if statement is meant to convert each letter's position in the list into an index like the grid squares have.
if gridsquare.index == ((number_no + 1) % (aspect_ratio[0]), ((number_no + 1) // (aspect_ratio[0] + 1)) + 1):
#create a list of squares that need to be updated, and a list of squares to be deleted
old_gridsquares.append(gridsquare)
flagged_gridsquare = GridSquare((number_no + 1) % (aspect_ratio[0]), ((number_no + 1) // (aspect_ratio[0] + 1)) + 1)
flagged_gridsquare.land = 1
#this part is meant to set the flag for the gridsquare that indicates if it is on the far side or the near side,
#if it is land
if number == "1":
flagged_gridsquare.near = 1
grid_changes.append(flagged_gridsquare)
#deletes from grid any items with a key that matches the old squares, and adds updated versions.
for old_gridsquare in old_gridsquares:
try:
del grid[old_gridsquare]
except:
print(old_gridsquare.index)
print(old_gridsquare.land)
for grid_change in grid_changes:
grid[grid_change] = None
I'm working on a "word generator" But I have a problem.
Sometimes it creates unwanted words.
I would like to remove them but not to heavy the loop.
I have to do like the example in the code with the whole alphabet.
There would be nothing wrong with it that much, but there are a few other problems that I would have dealt with if only I knew how to cut it short.
# alph = "abcdefghijklmnopqrstuvwyzxÄ…"
my_var = ["abc", "aabc", "cbd", "ccbd", "qwe", "qqwe"]
my_var2 = []
def removeDup():
for x in my_var:
if x.find("aa") == -1 and x.find("cc") == -1 and x.find("qq") == -1:
my_var2.append(x)
print(my_var2)
removeDup()
My idea is dynamic variables, but I can't make one loop in the other without creating chaos
I tried something like the one in the picture, but I can only take out words with repeated letters
There's no need for dynamic variables. Just make a list of all the duplicate characters.
dups = [z*2 for z in alph]
for x in open('xxx.txt', encoding='utf-8'):
if not any(dup in x for dup in dups):
print(x.strip())
I was working on building a randomized character generator for Pathfinder 3.5 and got stuck.
I am using the Populate_Skills(Skill_String, Draw, Skill_List, Class_Skill): function to populate a randiomized list of skills with their class based points total, class bonus, and point buy. So modelling the action of a player picking skills for their character.
As an example below, Wizards.
I pick Knowledge_Arcana as a skill and spend one of my skill point pool (Calculated by taking my intelligence modifier +2) on it. So that skill now equals my intelligence modifier(+1 in this case), class skill bonus as a wizard (+3), plus the point I spent(+1) for a total of 5.
The problem is while the function prints the correct result of 5, the outstanding variables do not populate with the final total. To continue our example I'd run the function on Knowledge_Arcana, get a +5, and then check the Knowledge_Arcana after the function call and get just +1. Conversely, if I write out the function as just an if statement it works. Example is next to the function for comparison.
Does anyone know why Im getting the different result?
## Creating the lists and breaking into two separate sections
Int_Mod = 1
Skill_Ranks = 3
Rand_Class = 'Wizard'
Knowledge_Arcana = Int_Mod
Knowledge_Dungeoneering = Int_Mod
Wizard_Class_Top_Skills = ["Knowledge_Arcana"]
Wizard_Class_Less_Skills = ["Knowledge_Dungeoneering"]
Class_Skill = 3
Important_Skills_Weighted = .6
Less_Important_Skills_Weighted = .4
Important_Skills_Total_Weighted = round(Skill_Ranks*Important_Skills_Weighted)
Less_Skill_Total_Weighted = round(Skill_Ranks*Less_Important_Skills_Weighted)
Wiz_Draw =['Knowledge_Arcana', 'Knowledge_Dungeoneering']
def Populate_Skills(Skill_String, Draw, Skill_List, Class_Skill):
if Skill_String in Draw:
Skill_List = Skill_List + Class_Skill + Draw.count(Skill_String)
print(Skill_String, Skill_List)
else:
print('Nuts!')
## Function Calls
Populate_Skills('Knowledge_Arcana', Wiz_Draw, Knowledge_Arcana, Class_Skill)
Populate_Skills('Knowledge_Dungeoneering', Wiz_Draw, Knowledge_Dungeoneering, Class_Skill)
print(Knowledge_Arcana,Knowledge_Dungeoneering)
Edited to be a MRE, I believe. Sorry folks, Im new.
You are passing in a reference to a list and expect the function to modify it; but you are reassigning the variable inside the function which creates a local variable, which is then lost when the function is exited. You want to manipulate the same variable which the caller passed in, instead.
def Populate_Skills(Skill_String, Draw, Skill_List, Class_Skill):
if Skill_String in Draw:
Skill_List.extend(Class_Skill + Draw.count(Skill_String))
print(Skill_String, Skill_List)
else:
print('Nuts!')
Alternatively, have the function return the new value, and mandate for the caller to pick it up and assign it to the variable.
def Populate_Skills(Skill_String, Draw, Skill_List, Class_Skill):
if Skill_String in Draw:
Skill_List = Skill_List + Class_Skill + Draw.count(Skill_String)
print(Skill_String, Skill_List)
else:
print('Nuts!')
return Skill_List
Skill_List = Populate_Skills('Knowledge_Arcana', Wiz_Draw, Knowledge_Arcana, Class_Skill)
# etc
You should probably also rename your variables (capital letters should be used for classes and globals; regular Python functions and variables should use snake_case) and avoid using global variables at all. The entire program looks like you should probably look into refactoring it into objects, but that's far beyond the scope of what you are asking.
My brother and I are creating a simple text editor that changes entries to pig latin using Python. Code below:
our_word = ("cat")
vowels = ("a","e","i","o","u")
#remember I have to compare variables not strings
way = "way"
for i in range(len(our_word)):
for j in range (len(vowels)):
#checking if there is any vowel present
if our_word[i] == vowels[j]:
# if there were to be any vowels our_word[i] wil now be changed with way
#.replace is our function the dot is what notates this in the python library
our_word = our_word.replace(our_word[i], way)
print(our_word)
Right now we're testing the word 'cat' but the program when run returns the following:
/Users/x/PycharmProjects/pythonProject3/venv/bin/python /Users/x/PycharmProjects/pythonProject3/main.py
cwwayyt
Process finished with exit code 0
We're not sure why there is a double 'w' and a double 'y'. It seems the word 'cat' is edited once to 'cwayt' and then a second time to 'cwwayyt'.
Any suggestions are welcome!
The problem arises from the fact that on the next iteration of for loop after doing the substitution, you are looking at the next position, which is part of the way that you just substituted into place. Instead, you need to skip past this. You would also experience another problem, that it only loops up to the original length, rather than the new increased length. You are probably better in this situation to use a while loop with an index variable that you can manipulate to point to the correct place as needed. For example:
our_word = "cat"
vowels = "aeiou"
way = "way"
i = 0
while i < len(our_word):
if our_word[i] in vowels:
our_word = our_word[:i] + way + our_word[i + 1:]
i += len(way) # <=== if you made a substitution, skip over the bit
# that you just substituted in place
else:
i += 1 # <=== if you didn't make any substitution
# just go to the next position next time
print(our_word)
Im writing an algorithm in Python which plays this game.
The current state of the board of tiles in the game is a dictionary in the form of:
{
<tile_id>: {
'counters': <number of counters on tile or None>,
'player': <player id of the player who holds the tile or None>,
'neighbours': <list of ids of neighbouring tile>
},
...
}
I have another dictionary which stores all of my tiles which are 'full' (i.e. a tile which has one less counter than its number of neighbours and where the player is me) This dictionary, full_tiles, is in the same form as the board dictionary above.
I am now trying to create a list, chains, where each element in the list is a dictionary of my full tiles that are neighbouring at least one other full tile (i.e a chain of full tiles). So this will be a list of all my seperate chains on the board.
Here is my code so far:
for tile_id, tile in full_tiles.items(): #iterates through all full tiles
current_tile = {tile_id : tile} #temporarily stores current tile
if not chains: #if chains list is empty
chains.append(current_tile) #begin list
else: #if list is not empty
for index, chain in enumerate(chains): #iterate though list of chains
if not (current_tile in chain): #if current tile is not in current chain
for tile_id2, tile2 in chain.items(): #iterate through tiles in current chain
for neighbour in tile2["neighbours"]: #iterate through each neighbour of current tile
#neighbour is in the form of tile_id
if neighbour in chain: #if current tile's neighbour is in chain
chain[tile_id] = tile #add tile to chain
It is very difficult for me to test and debug my code and check if it is working correctly as the code can only be run in an application that simulates the game. As you can see, there is quite a lot going on in this block of code with all of the nested loops which are difficult to follow. I cannot seem to think straight at the minute and so I cannot determine if this mess, in all honesty, will function as I hope.
While I am writing this, I have just realised that - on line 7 of this code - I am only checking if the current tile is not in the current chain and so there will be intersecting chains which, of course, will be a mess. Instead of this, I need to first check if the current tile is in not in any of the chains, not just one.
Apart from this error, will my code achieve what I am attempting? Or can you recommend a simpler, neater way to do it? (There has to be!)
Also, let me know if I have not given enough information on how the code is run or if I need to explain anything further, such as the contents of the board dictionary.
Thank you for any help in advance.
EDIT: Unfortunately, I was under a time constraint to complete this project, and as it was my first time ever working with Python, I currently lack the knowledge in the language to optimise my solution using the sources given below. Here is my final extremely ugly and messy solution to this problem which, in the end, worked fine and wasn't terribly inefficient given the small data set that the code works on.
for x in range(0, len(my_hexplode_chains) - 1):
match_found = False
for y in range(x + 1, len(my_hexplode_chains)):
for tile_id_x, tile_x in my_hexplode_chains[x].items(): #compare each chain in list
for tile_id_y, tile_y in my_hexplode_chains[y].items(): #to every other chain
for neighbour in tile_x["neighbours"]: #if tiles in different lists
if neighbour == tile_id_y: #are neighbours
match_found = True
my_hexplode_chains[x].update(my_hexplode_chains[y]) #append one chain to the other
del my_hexplode_chains[y] #delete appended chain
if match_found: #continue loop at next chain
break #very ugly way to do this
if match_found:
break
if match_found:
break
if match_found:
break
How about this optimization?
def find_match (my_hexplode_chains):
x = 0
len_chain = len(my_hexplode_chains)
while x <= len_chain:
y = x + 1
for tile_id_x, tile_x in my_hexplode_chains[x].items():
for tile_id_y, tile_y in my_hexplode_chains[y].items():
if tile_id_y in tile_x["neighbours"]:
my_hexplode_chains[x].update(my_hexplode_chains[y])
del my_hexplode_chains[y]
return True
x += 1
return False
You could pass this function after each move in your game and trace the output.