Using AND OR Operators In The Same If Statement (Python) - python

So, I'm challenging myself to build an A.I enabled chess game. Right now I'm just working on the game logic taking an object oriented approach. It's proving to be a good way of going about it. However, while I was defining a method in my pawn object to capture another piece, I needed to use a long if..and..and..or statement. Now it's not preforming the way I need it to. I'm assuming that's because of a rule having to do with operators and how they evaluate together. I seem to remember something about this in one of my college IT courses I just finished. I did some testing on the algorithm and can confirm that the problem is contained in the last few expressions of the first line of the following elif statement :
#Checks to see if there is a piece in the desired move position. Each square is represented by (x,y) co-ordinates which are stored as an object property and entered into a dictionary with the piece name as the key and the square it occupies as the value.
elif checkSquare(peicePosition, move) == True and move[1] - self.position[1] == 1 and move[0] == self.position[0] + 1 or move[0] == self.position[0] - 1:
self.position = move
self.firstMove = False
peicePosition[self.name] = self.position
return print("Capptured Peice")

You're probably running into an order of operations issue. Just like the PEMDAS that we all learned in middle school (or some equivalent), all of the AND operations are performed before the OR. This site details the order of operations for logic as well as math. Try adding some parentheses, just as the comments state.

Related

"For" loop Python code - does this look right/acceptable?

I would like to create a "for" loop (in Python 2). I have a list of obstacles and for each obstacle, if they are true (i.e. exist and appear in the list) I would like to append them to a list called "tests" and call a function called "obstacle_detection" (which deals with what happens when an obstacle is detected) (and I use "tests" later). This is part of a much larger program and I can't quite tell whether it's working, so I was wondering if anyone would be able to tell me if it makes sense? Or advise me of a better way there might be of doing this?
obstacles = [obstacle, obstacle1, obstacle2]
tests = []
counter = 0
for obstacle in obstacles:
tests.append(0)
tests[counter] = obstacle_detection(obstacle, pos)
counter = counter + 1
Your code possibly makes sense, depending on how you define obstacles and obstacle_detection.
As it is, you could write your code this way :
tests = [obstacle_detection(obstacle, pos) for obstacle in obstacles]
It creates a new list automatically, with the same length as obstacles and filled with obstacle_detection values for each obstacle.

Sprite Values Must Move

I have a sprite and when it collides with another object I want it to increase the score that the user gets. However I am unsure how to export the fact that the sprite has collided to another part of my program. This is what I tried:
def checkCollision(self, sprite):
if self.rect.colliderect(sprite.rect):
self.x=3000
self.score=score+100
However it ignore the fact the score has increased despite me having made score global.
Any help would be appreciated.
I'm assuming that the collision function you provided is part of your player class. Making that assumption, you could simply assign the global score to = x.score (x = whatever you named that particular player object) Obviously this is less than ideal but it's a simple way to stick in the functionality you're looking for.

How to brute force a tree of 'yes/no' decisions?

I want to solve a puzzle. But I simply don't know what kind of code is required for it. The problem is an exponential one.
Description:
The Player walks/runs one step at a time. Sometimes there will be a decision to be made; that is a yes/no question. Once the question is answered, the player continues walking until the next decision/question is reached. Continue this until the total distance is covered.
The problem is I want to see every possible route through this (many python lists such as ['y','y','n','y','n']). Here is the code I have written so far: (the Player is in a Player() class, I have removed it because it is unimportant here.)
class Solver(object):
""" Solver object. """
def __init__(self, field):
self.field = field
self.dinc = 113
self.distance = 128
def take_step(self, player):
""" Takes a step and records players route. """
# Adds 0 if there is no decision to be made on this step
# Adds 1 if there is a decision to be made on this step
player.run(self.dinc)
if self._is_decision_time(player):
player.route.append((player.step_id, 1))
else:
player.route.append((player.step_id, 0))
def next_decision(self, player):
""" Accepts a player object. Walks until reaches next decision. """
while not self._is_decision_time(player):
self.take_step(player)
self.display_stats(player)
def say_no(self, player):
""" Simulates a no response. Resets danger. Updates route with decision. """
player.route[-1] = (player.step_id, 'n')
player.danger = 0
print 'no!'
def say_yes(self, player):
""" Simulates a yes response. Updates route with decision. """
player.route[-1] = (player.step_id, 'y')
print 'yes!'
The solution of what I'm looking for is like this:
Walk until a question is reached
Make a copy of the route
On route A say Yes
On route B (the copy) say No
Route A:
repeat what is above (this forks another two routes)
Route B:
repeat what is above (this forks another two routes)
Using the code I have so far, it is something like:
route_a = Player()
solver = Solver()
# walk until a battle is reached
solver.next_decision(route_a)
# make a copy of the route (there are now two routes A + route B)
route_b = copy.deepcopy(route_a)
# on route A say 'yes'
solver.say_yes(route_a)
# on route B say 'no'
solver.say_no(route_b)
# walk until the next decision is reached
solver.next_battle(route_a)
solver.next_battle(route_b)
# Then?
This problem is exponential, because at each decision the route forks into two more routes. I need all of the possibilities; I happen to know there are less than 512 possibilities so a computer can solve it in an instant.
Each route will be stored in a Player.route instance (as a list, eg: ['y','y','n','y'])
I just have no idea how to solve a problem like this programmatically. I would appreciate some ideas as to how to structure code to solve a problem like this.
Actually, such a data structure -- a binary tree -- is used in order to just avoid the exponential problem you mentioned. Or, in other words, the supposed list of 'y' and 'n' will grow exponentially, but usually you don't need it, because you have the binary tree. You know that you get each way by a yes-or-no question.
But, if you want to print the list you were asking for, do it like in this pseudo-code (since I'm lost to C++, I still can't program in Python ;-) )
def recurse(node, s=''):
if node == end_node:
print s
return
recurse(node.left, s + 'n')
recurse(node.right, s + 'y')
Then invoke the function starting at the root or head node, i.e. recurse(root_node).
This seems like a fairly easy job for recursion and you already have a pretty simple tree structure. Perhaps you should just migrate the decision process into an explicit tree structure and implement a recursive traversal of the nodes? That's probably the most prudent solution given what it looks like you're trying to do. I guess you're looking for "brute force"... but the alternate iterative solution would be a lot less elegant (and harder to write).
A naive, but probably a suitable way to generate permutations like this is to run through numbers 0..512, convert them to binary (with right padding) and treat zeroes as 'No' and ones as 'Yes'. Nine bits is enough for 512 values, so generate a string like this:
'{0:0>9b}'.format(123)

Chess negamax function

Hi!
I'm trying to write a negamax search algorithm for my chess engine, but i can't seem to get it working. I'm using wikipedias pseudocode as an example, but somehow it doesn't produce the expected results. When i run it with a ply of 2, it changes my board data structure, although it shouldn't. After the function is finished running with ply 2, all of the white's (or black's. depends what player calls the function.) pawns are moved 2 spaces forward from starting position.
My make and unmake move functions are working perfectly , as i tested them with a non-recursive function that searches up to 5-ply. Then, it worked perfectly. There must be something wrong with my negamax implementation.
Thank you very much for your help!
def negaMax(self, board, rules, ply, player):
""" Implements a minimax algorithm. """
if ply == 0:
return self.positionEvaluation()
self.max_eval = float('-infinity')
self.move_list = board.generateMoves(rules, player)
for self.move in self.move_list:
board.makeMove(self.move, player)
self.eval = -self.negaMax(board, rules, ply - 1, board.getOtherPlayer(player))
board.unmakeMove(self.move, player)
if self.eval > self.max_eval:
self.max_eval = self.eval
return self.max_eval
The main issue here is I believe the usage of object variables instead of local variable.
self.move is an object variable, every time you change it - every level of the recursion "sees" the change, which is usually a bad thing for recursive algorithms.
Recursive algorithms should be self contained, and do minimal if any change on the calling environment - it makes it much easier to walk over the flow of the algorithm.
The two main issues I see in this code are:
self.move should be a local variable (declare it as move). When you later do: board.unmakeMove(self.move, player) - I suspect that the board is undoing a different move, which was set deeper in the recursion tree, and not the one you intended. Using a local variable will eliminate this undesired behavior.
Each level of the recursive call is setting self.max_eval = float('-infinity') - so you constantly change it, though it is probably not what you want to do.
The solution should be something like that:
def negaMax(self, board, rules, ply, player):
""" Implements a minimax algorithm. """
if ply == 0:
return self.positionEvaluation()
max_eval = float('-infinity')
move_list = board.generateMoves(rules, player)
for move in move_list:
board.makeMove(move, player)
currentEval = -self.negaMax(board, rules, ply - 1, board.getOtherPlayer(player))
board.unmakeMove(move, player)
if currentEval > max_eval:
max_eval = currentEval
return max_eval
I am not 100% certain it will indeed solve everything in the code (but it will solve some of it) but I am 100% certain avoiding object variables will make your code much easier to understand and debug.

Why can I not delete list elements while iterating in an OO setting?

My situation is as follows: I'm working on an implementation of BlackJack. I've got classes for Card, Hand, Player, Deck, and Game. The main game object stores players and a deck, while players stores hands which store cards.
I often do something like the following. In this example I am dealing the initial cards to each player.
num_cards = 2
for player in self.players:
new_hand = Hand()
for i in range(num_cards):
new_hand.add_card(self.deck.get_next_card())
player.assign_hand(new_hand)
This works splendidly. My problem now is that I wish to delete a hand from a player's set of hands (a player can split in BlackJack, causing more than one hand to be generated). In the following function, I intend to loop through each player's hands. If the value of the hand is greater than 21, I want to delete the hand. (Note that the remove() functionality below is normally performed in the Player class, called via a Player method named fold_hand(). I was having the same problem, so I have moved the code to somewhere more visible for expository purposes.)
for player in self.players:
for hand in player.hands:
if hand.smallest_value() > 21:
player.hands.remove(hand)
This does not work. To be clear, I am able to print out the hand before the remove() line and it does not print out after. That is, it seems to be removed. However, the in the next turn of play, the hand is back again. Thus the players' hands grow every turn.
The above code is in a function called validate_player_hands() in the Game class. This function is called from a file called play.py, which exists to start/end the game and facilitate the primary game loop. Thus, the only call to validate_player_hands() is in the play.py file, one indent in, in the game loop. I call:
game.validate_player_hands()
I have also tried finding the index of the hand and using the 'del' keyword, but the result is the same.
Why would the list element (a Hand object in a list called player.hands) fail to delete when it looks like it has been deleted?
Thanks in advance,
ParagonRG
How about using a simple list comprehension to eliminate hands:
for player in self.players:
player.hands = [hand for hand in player.hands if hand.smallest_value() <= 21]
EDIT
With filter:
for player in self.players:
player.hands = filter(lambda x: x.smallest_value() <= 21, player.hands)
Create a copy and iterate over the the object with an index tied to the object length. Set every elements to be deleted with a 0 and then filter the hands to purge the zeros.
for player in self.players:
for hand_idx in range(len(player.hands)):
if player.hands[hand_idx].smallest_value() > 21:
player.hands[hand_idx]=0
player.hands=filter(None,hands)
You will want to do something like the following:
newList = hands[:]
for hand in newList:
if hand.smallest_value() > 21: #your code here
player.hands.remove(hand)
This allows you to make a copy of the list to be modified while iterating over that copy thus avoiding the "tree limb" scenario that Mihai mentioned.
This can be solved by adding [:] to the end of the "for" statement. This creates a copy of the list. You can then loop over the copy, while changing the original list:
for player in self.players:
for hand in player.hands[:]:
if hand.smallest_value() > 21:
player.hands.remove(hand)
(This is Python's list slicing syntax. It's one of the fastest way to copy a list. More often this syntax is used in the form some_list[3:8] to get the list entries from index 3 to 7 inclusive, but by leaving out the first number you can get everything from the start of the list, and by leaving out the last number you get everything up to the end of the list).

Categories