How do I create a reversi function that encloses opponents tiles? - python

I have an assignment for a programming unit that asks this:
create the function enclosing(board, player, pos, direct), that represents the rules of Reversi according to the following specifications.
A board position is represented by a pair (r, c) where r and c are each integers from the range
range(8) representing a row index and a column index, respectively. For instance, the position \b3" is
represented by (2, 1). The row and column order of the \b3" style and the specication of position
is reversed. This is because it is natural to constuct lists of lists in such a way that the outer lists
correspond to rows, and the inner list positions corerspond to column positions, so we reference them
with row rst, then column. This row, column convention is quite common across maths and computer
science.
A direction is represented by a pair (dr, dc) where dr and dc are each integers from the set f-1, 0,
1g. For instance, \horizontal to the left" is described by (0, -1), \diagonal to the right and down" is
described by (1, 1), and so on).
The function enclosing(board, player, pos, dir) represents whether putting a player's stone on
a given position would enclose a straight line of opponent's stones in a given direction:
Input: A board conguration board, an integer player from the set f1, 2g, a board position pos and a direction dir.
Output: True if board contains a stone of player in direction dir from position pos and all positions
on the straight line between that stone and pos contain stones of the other player, and there is at
least one stone belonging to the other player on the straight line; False otherwise.
This is what I have:
def enclosing(board, player, pos, direct):
if player == 1:
a = direct[0]
b = direct[1]
i = 1
while i < 8:
newpos = (pos[0] + i*a , pos[1] + i*b)
if board[newpos[0]][newpos[1]] == 1:
return True
elif board[newpos[0]][newpos[1]] == 2:
i = i + 1
else:
return False
Also keep in mind this is a beginners course and I have about a months experience on python.

The code in your edit looks good, and is what I was getting at. Good luck with the rest of the project!
A couple edge cases:
board[newpos[0]][newpos[1]] will go out of bounds
Your function will return True in the case XOOOOX, which is not a capture by Othello rules (not sure if your assignment defines it differently
I strongly recommend writing a couple simple tests just to make sure your code works. They don't need to cover the full range of cases, and doesn't need to be the full board. It's usually not required, but makes it easier to evaluate: just hit "run" instead of trying to reason through your code.
Here's an example:
def test():
assert enclosing([[0, 0, 0], [0,0,0],[0,1,2]], 1, (0,2), (1,1)) == True

Related

Robot return to origin

Question:
There is a robot starting at position (0, 0), the origin, on a 2D plane. Given a sequence of its moves, judge if this robot ends up at (0, 0) after it completes its moves.
The move sequence is represented by a string, and the character moves[i] represents its ith move. Valid moves are R (right), L (left), U (up), and D (down). If the robot returns to the origin after it finishes all of its moves, return true. Otherwise, return false.
Note: The way that the robot is "facing" is irrelevant. "R" will always make the robot move to the right once, "L" will always make it move left, etc. Also, assume that the magnitude of the robot's movement is the same for each move.
Input: moves = "UD"
Output: true
Explanation: The robot moves up once, and then down once.
All moves have the same magnitude, so it ended up at the origin where it started.
Therefore, we return true.
I have the following solution, which seems to be wrong for sequences = "UD", which should return True. Could someone help me understand what I am doing wrong here and how I could fix it?
class Solution:
class Mover:
def __init__(self, x, y):
self.x, self.y = x, y
def new_pos(self, x, y):
return x + self.x, y + self.y
WALKS = dict(U=Mover(0, -1), D=Mover(0, 1),
L=Mover(-1, 0), R=Mover(1, 0))
def judge_circle(self, moves):
x = y = 0
for id in moves:
x, y = self.WALKS[id].new_pos(x, y)
return x == y == 0
def move_sequences(self,sequences):
for moves in sequences:
return (solution.judge_circle(moves))
if __name__ == "__main__":
solution = Solution()
sequences = "UD"
print(solution.move_sequences(sequences))
This solution seems overthinking it by quite a bit. You can just make a counter of each of the 4 directions and figure out if you have the same number of Us relative to Ds and Ls relative to Rs. return s.count("U") == s.count("D") and s.count("L") == s.count("R") gives you a linear solution that can be optimized into a single pass with something like
from collections import Counter
d = Counter(moves)
return d["D"] == d["U"] and d["R"] == d["L"]
As for your code,
for moves in sequences:
return (solution.judge_circle(moves))
looks funny to me. Returning on the first iteration means the loop is pointless. moves here is misleadingly named -- it's only a single character "U". judge_circle already does a loop, so if you really want to brute-force it, you'll only want one loop over the sequence rather than two.
Your task is simple:
def judge_circle(moves):
if moves.lower().count('U') == moves.lower().count('D') and moves.lower().count('L') == moves.lower().count('R'):
return True
else:
return False
print(judge_circle('UD'))
You only have to check whether the number of 'ups' equals the numbers of 'downs', and 'lefts' equal 'rights'.
Ok, a part from refactor advices, you can fix your script in a easy way.
def move_sequences(self,sequences):
for moves in sequences:
return (solution.judge_circle(moves))
fails because you pass a string in sequences and the for loop cycles over the letters, passing every single letter to judge_circle.
Remove the for loop and pass sequences to judge_circle!

What would be the OOP way to solve this?

I am trying to solve this problem.
There is a robot starting at position (0, 0), the origin, on a 2D plane. Given a sequence of its moves, judge if this robot ends up at (0, 0) after it completes its moves.
The move sequence is represented by a string, and the character moves[i] represents its ith move. Valid moves are R (right), L (left), U (up), and D (down). If the robot returns to the origin after it finishes all of its moves, return true. Otherwise, return false.
Note: The way that the robot is "facing" is irrelevant. "R" will always make the robot move to the right once, "L" will always make it move left, etc. Also, assume that the magnitude of the robot's movement is the same for each move.
Example 1:
Input: moves = "UD"
Output: true
Explanation: The robot moves up once, and then down once. All moves have the same magnitude, so it ended up at the origin where it started. Therefore, we return true.
Example 2:
Input: moves = "LL"
Output: false
Explanation: The robot moves left twice. It ends up two "moves" to the left of the origin. We return false because it is not at the origin at the end of its moves.
Example 3:
Input: moves = "RRDD"
Output: false
Example 4:
Input: moves = "LDRRLRUULR"
Output: false
Even though I solved it using counters as well as conditionals, I would like to see how I could use Python's OOP components to solve this. How can I separate out the valid moves, which are R (right), L (left), U (up), and D (down) into separate methods for better abstraction, so that I could add more methods in future? How would the refactored code look like? This is the solution:
class Solution(object):
def judgeCircle(self, moves):
x = y = 0
for move in moves:
if move == 'U': y -= 1
elif move == 'D': y += 1
elif move == 'L': x -= 1
elif move == 'R': x += 1
return x == y == 0
Here's one way to do it by encapsulating the data and the code that acts on it — both key OOP features. Note though that using "getters" and "setters" is not "pythonic" (because they are generally unnecessary and can be added retroactively if the need ever arises for some reason).
class Solution:
class Mover:
def __init__(self, x, y):
self.x, self.y = x, y
def new_pos(self, x, y):
return x + self.x, y + self.y
WALKS = dict(U=Mover(0, -1), D=Mover(0, 1),
L=Mover(-1, 0), R=Mover(1, 0))
def judge_circle(self, moves):
x = y = 0
for id in moves:
x, y = self.WALKS[id].new_pos(x, y)
return x == y == 0
solution = Solution()
sequences = "UD", "LL", "RRDD", "LDRRLRUULR"
for moves in sequences:
print(solution.judge_circle(moves))

Dynamic programming solution to the shortest distance two-finger typing problem

I'm trying to educate myself better about dynamic programming, and hoping to do so by attempting to solve the following problem (for reference here's a solution to it).
You have a keyboard layout as shown above in the XY plane, where each English uppercase letter is located at some coordinate, for example, the letter A is located at coordinate (0,0), the letter B is located at coordinate (0,1), the letter P is located at coordinate (2,3) and the letter Z is located at coordinate (4,1).
Given the string word, return the minimum total distance to type such string using only two fingers. The distance between coordinates (x1,y1) and (x2,y2) is |x1 - x2| + |y1 - y2|.
Note that the initial positions of your two fingers are considered free so don't count towards your total distance, also your two fingers do not have to start at the first letter or the first two letters.
For example for the input word "HAPPY" we would have:
Output: 6
Explanation:
Using two fingers, one optimal way to type "HAPPY" is:
Finger 1 on letter 'H' -> cost = 0
Finger 1 on letter 'A' -> cost = Distance from letter 'H' to letter 'A' = 2
Finger 2 on letter 'P' -> cost = 0
Finger 2 on letter 'P' -> cost = Distance from letter 'P' to letter 'P' = 0
Finger 1 on letter 'Y' -> cost = Distance from letter 'A' to letter 'Y' = 4
Total distance = 6
From what I read online there are 1D, 2D and 3D (in terms of space) dynamic programming solutions to the above problem. I have found 1D and 2D solutions online to this problem, but I find them too difficult to follow, so I'm hoping to start with the 3D one and gradually understand more efficient ones.
What's a 3D DP formulation to this problem? Does this problem have a name in particular?
I understand the recursive nature of the problem, but I'm struggling to formulate a simple bottom-up solution (e.g. in 3D).
A fairly simple 3-D dynamic programming approach is the following:
def dist(a,b): # gets distance between chars a and b
y1,x1 = a/6,a%6
y2,x2 = b/6,b%6
return abs(y1-y2)+abs(x1-x2)
def solve(s):
N = len(s)
dp = [[[float('inf') for x in range(26)] for x in range(26)] for x in range(N+1)]
dp[0] = [[0 for x in range(26)] for x in range(26)]
for i in range(N):
cur = ord(s[i])-ord('A')
for j in range(26):
for k in range(26):
dp[i+1][j][cur] = min(dp[i+1][j][cur], dp[i][j][k] + dist(k,cur)) # move right finger
dp[i+1][cur][k] = min(dp[i+1][cur][k], dp[i][j][k] + dist(j,cur)) # move left finger
res = float('inf')
for i in dp[N]:
res = min(res,min(i))
return res
In the solve function, we declare a dynamic programming table dp[N+1][26][26], and let's store at cell dp[i][j][k] the minimum distance required to type all characters in the string up to but not including the i'th character, with the left finger finishing on the j'th key and the right finger on the k'th key.
Our base case is at i = 0, we know it takes 0 total distance to have our fingers start anywhere, so the first row is initialized entirely with 0s. Then, the transition is from all possible configurations of the two fingers to either moving the left finger or the right finger to the new key. If you're currently at state dp[i][j][k], after pressing the i'th key (let's call it cur), if we press this new key with our left finger, the updated state is dp[i+1][cur][k], since the left finger moved from j to cur. Similarly, if we press it with our right finger, the updated state is dp[i+1][j][cur].
Our final answer is found somewhere in the N+1'th row, where N is the length of the string. We simply take the minimum of all combinations of left and right fingers in this row.
EDIT:
Here's the 2-D solution I described in the comments:
def solve(s):
N = len(s)
dp = [[float('inf') for x in range(26)]for x in range(N)]
dp[0] = [0 for x in range(26)]
for i in range(1,N):
cur = ord(s[i])-ord('A')
lst = ord(s[i-1])-ord('A')
for j in range(26): # one finger currently on s[i-1], other finger on j
dp[i][j] = min(dp[i][j], dp[i-1][j] + dist(lst,cur)) # move first finger, so second finger remains on j
dp[i][lst] = min(dp[i][lst], dp[i-1][j] + dist(j,cur)) # move second finger, so second finger becomes the new "first finger"
# and now the old "first finger" becomes the new "second finger"
res = min(dp[N-1])
return res

Using the same function for a different chess piece

Recently I was given a programming assignment to mimic the 8 queens problem which I'm sure you are all aware of and was tasked with creating functions for different pieces on the board. For example, placing a queen on the board in such a way that she won't see other queens.
Below you'll find my first function, queensees, which basically just highlights the queen's position on the board and identifies whether or not she can see others.
def queenSees(pos,size):
""" Return a list of all squares"In view" of a queen in position pos on a board of size"""
inView=[]
#Row and column
for i in range(size):
#Column
setAppend(inView,(i,pos[1]))
#Row
setAppend(inView,(pos[0],i))
#Diagonals
for r in [-1,1]:
for c in [-1,1]:
appendIfInBounds(inView, pointShift(pos,r*i,c*i), size)
#Take out position of queen so she doesn't see herself...
inView.remove(pos)
Below I've also written a function for the rook piece because it was simply a matter of taking the diagonal search out of the equation.
def rooksees(pos,size):
""" Return a list of all squares"In view" of a rook in position pos on a board of size"""
inView=[]
#Row and column
for i in range(size):
#Column
setAppend(inView,(i,pos[1]))
#Row
setAppend(inView,(pos[0],i))
appendIfInBounds(inView, pointShift(pos,r*i,c*i), size)
#Take out position of queen so he doesn't see himself...
inView.remove(pos)
How would I modify this to accommodate the knight chess piece?
A knight moves one of two distances in a cardinal direction, and the other of those two distances in an orthogonal direction. 4 original directions * 2 orthogonal directions each * 2 original distances = 16 possible places. It looks like you already know how to filter out theoretical positions that are actually off the board, so all you need to do is generate those 16 positions in the first place. If you can't see a way to come up with those programaticaly, you could just hard code them like so:
positions = [[pos[0]+2,pos[1]+1],[pos[0]+2,pos[1]-1] ... ]
But this is very prone to minor typos leading to hard-to-track bugs. A better way might be to just hard code the "adjustments":
moves = [(2,1),(2,-1),(-2,1)(-2,-1) ... ]
And then use a loop to apply them to the current position:
positions = [[pos[0]+d[0],pos[1]+d[1]] for d in moves]
all you need to do for the rook the comment out the diagonal code.
I having trouble implementing a knight see even though I know the coordinates: can anyone tell me how to change the queensees function to display the knight - remember i have commented out the queensees function element i to try and display a knights movements which is not working.
def makeBoard(size):
board=[]
for i in range(size):
board.append([])
for j in range(size):
board[-1].append(False)
return board
def displayBoard(b):
divider=("+---"*len(b))+"+"
for row in b:
print divider
print "|"+"|".join({True:" X ",False:" "}[i] for i in row)+"|"
print divider
def setAppend(s,i):
""" Add i to s unless i is already in s """
if not i in s: s.append(i)
def inBoard(p,size):
""" if point is valid for a board of given size, return True. Else return False """
if 0<=p[0]
def pointShift(p,r,c):
""" Return position of cell r,c away from given point p """
return (p[0]+r,p[1]+c)
def appendIfInBounds(s,p,size):
""" If point p is within the bounds of a board of given size, append to s unless it's already there """
if inBoard(p,size):
setAppend(s,p)
def queenSees(pos,size):
""" Return a list of all squares "In view" of a queen in position pos on a board of size"""
inView=[]
moves = [(-2, -1), (-2, +1), (+2, -1), (+2, +1), (-1, -2), (-1, +2), (+1, -2), (+1, +2)]
#Row and column
for i in range(size):
setAppend(inview,(moves[i]))
#Column
#setAppend(inView,(i,pos[1]))
#Row
#setAppend(inView,(pos[0],i))
#Diagonals
#for r in [-1,1]:
#for c in [-1,1]:
# appendIfInBounds(inView, pointShift(pos,r*i,c*i), size)
#Take out position of queen so she doesn't see herself...
inView.remove(pos)
return inView
def hasQueen(board, points):
""" Returns True if any of the given points on the given board contain a queen """
for p in points:
if board[p[0]][p[1]]:
return True
return False
def cloneBoard(b,size):
""" Make a copy of a board. Boards are objects (lists are objects) so a=b just makes them refer to the same object..."""
c=makeBoard(size) #clone
for i in range(size):
for j in range(size):
c[i][j]=b[i][j]
return c
def fillBoardRecursion(board,row, size):
""" Given a board completed to given row, try all possible positions for next row and continue """
if row==size:
#Base case
return board
else:
for col in range(size):
#If we put a queen here, would it "see" another?
if not hasQueen(board,queenSees((row,col),size)):
b=cloneBoard(board,size)
b[row][col]=True
result= fillBoardRecursion(b,row+1,size)
if result!=False:
return result
return False #Failed at this point, so return False
b=makeBoard(8)
b=fillBoardRecursion(b,0,8)
displayBoard(b)

Fluid-like characteristics with Pygame

So I'm trying to write a little simulator thing with pygame, but im stuck
I'm trying to make particles that fall and settle but i want them to stack and stop when they hit a wall
This may sound confusing so here is an example:
http://www.coolmath-games.com/0-sugar-sugar/
I want to make particles that resemble the sugar in the game above:
I started by trying:
if pygame.sprite.spritecollideany(self, self.game.block_list):
self.rect.x += 0
self.rect.y += 0
but then the particles just stop and they don't roll down sloped surfaces
also I need to know how to check if a sprite collides with any other sprite in its group
So if anyone knows how I can duplicate this liquid like particle movement in pygame that would be awesome!
Thank You!
As I stated above, I tried to write the exactly same game with pygame too but left it uncompleted.
First of all, I preferred NOT to store these particles as different objects. Instead I used a dictionary to store their coordinates. I made it so because there are HUNDREDS of them and you have to check for collisions ~50 times per second for each of them. If you try to make them all different objects, it may go out of hand at some point.
After you detect a collision, in order to make them roll down sloped surface, let them move diagonally too. First check for the cell below, if that cell is not empty, check for the cells at bottom left and bottom right of the particle.
Btw, I found my function that moves the particles but it is not really readable.
def spawnSugar(spawnPoint) :
global sugarList,mapDict
mapDict[spawnPoint] = 1
sugarList.append(spawnPoint)
def moveAll() :
global mapDict,sugarList
sugarListTmp = sugarList
sugarList = []
for sugarX,sugarY in sugarListTmp :
# m stands for the vertical movement (1 for down, 0 for staying still)
# k stands for horizontal movement (-1 for left, +1 for right)
m = 1
if mapDict[( sugarX , (sugarY+1)%mapSizeY )]==0:
# checks whether the coordinate below this particle is empty
k = randint( -(mapDict[((sugarX-1)%mapSizeX , (sugarY+1)%mapSizeY)]==0) , mapDict[((sugarX+1)%mapSizeX , (sugarY+1)%mapSizeY)]==0 )
# If it is empty; randomly chooses 1 of the 3 coordinates below the particle (1 of them is just below and the other 2 are diagonally below)
elif mapDict[((sugarX-1)%mapSizeX,(sugarY+1)%mapSizeY)]==0 and mapDict[((sugarX-1)%mapSizeX,(sugarY)%mapSizeY)]==0 and mapDict[((sugarX+1)%mapSizeX,(sugarY+1)%mapSizeY)]==0 and mapDict[((sugarX+1)%mapSizeX,(sugarY)%mapSizeY)]==0:
# If the coordinate below the particle is not empty but other 2 diagonals are empty
k = -1 if randint(0,1) else 1 #chooses 1 of them randomly
else : # If at most 1 of these 2 diagonal coordinates are empty
k = (mapDict[((sugarX+1)%mapSizeX,(sugarY+1)%mapSizeY)]==0 and mapDict[((sugarX+1)%mapSizeX,(sugarY)%mapSizeY)]==0) or -(mapDict[((sugarX-1)%mapSizeX,(sugarY+1)%mapSizeY)]==0 and mapDict[((sugarX-1)%mapSizeX,(sugarY)%mapSizeY)]==0)
if not k: # If none of them are empty
m = 0
mapDict[(sugarX,sugarY)] = 0
mapDict[((sugarX+k)%mapSizeX,(sugarY+m)%mapSizeY)] = 1
sugarList.append(((sugarX+k)%mapSizeX,(sugarY+m)%mapSizeY))
# Values to assign before entering the main loop
mapDict = {}
sugarList = []
for x in range(mapSizeX):
for y in range(mapSizeY):
mapDict[(x,y)] = 0

Categories