for those who don't know the game: http://en.wikipedia.org/wiki/Chomp
short review- you have choclate table n x m with 1 poisoned cubed in the bottom left corner.
The player target is to avoid eating that cube. Each player in his turn choose 1 cube and actualy eat all the cubes that are right and up to that cube.
With a given matrix n X m and a list that represent the configuration of the game;
Each index represent how many cubes are in each column. For example: [3,3,2,2]- (the player chose to eat the second cube from the right)
P=poisoned
X X
X X X X
P X X X
(the numbers are just for the order)
I need to recursivley return an answer wheater there is a winning strategy or not for the player that it's turn to play. I thought about this: If I can know that for the next step (player against me) there's no winning strategy- I have winning strategy. stop terms are: if 1 last cube left- return false; if 2 last cubes left- return true. Just have hard times how to return it recursivley in a way that will give me an answer for that question
I thought about calculating all the moves that the player can do, but I couldn't implement that solution... any help?
Related
I have coded a minimax algorithm with alpha-beta pruning for a 4x4 Tic Tac Toe, in which the players who gets 3 marks in a row wins. However, it seems that the first to move wins in this game, such that minimax doesn't even try to make it harder for the player to win, because no other possible move can improve the outcome (since minimax assumes optimal play from both sides). Therefore, I added the condition that the algorithm will choose the best move that will also maximize the game length (while assuming that the other player will try to win with least moves), and I tried to do that by adding 2 more "alpha" and "beta" variables, but for the game length.
My game has worked fine without this new condition, but when I add it, it doesn't work properly, and the algorithm will not avoid losing, which minimax should have prevented. Below is the part of the code which solves for the optimal move.
Note that I used 1 and -1 to represent the players, that is, o = 1 and x = -1, and they are themselves the score of their win. A draw returns 0.
def optimalmove(board, player, alpha, beta,optlength,movementlength,worlength):
stator = checkstate4(board) # Contains the state of the game. stator[0] is whether the game has reached the end or not,
# stator[1] is who has won (or draw), stator[2] are the possible moves
if len(stator[2]) == boardsize ** 2:
# if it is the first move, pick the corner as it has already been calculated
# to be the optimal, in order to reduce time
return([0, [0, 0],movementlength])
if stator[0]: #If at the end return which one has won
return([stator[1], [],movementlength])
movement = [] #best movement
for move in stator[2]: #loops through each possible move
localboard = deepcopy(board) #creates a copy of the board to not change the original
localboard[move[0]][move[1]] = player
quality = optimalmove(localboard, player * (-1), alpha, beta,optlength,movementlength+1,worlength)
#since it made a move, increase the move list length by one
if player == o: #'O' maximizes the score and wants to reduce the length of the game
if (quality[0] > alpha) or ((quality[0] >= alpha) and (quality[2] < worlength)):
# If it finds a better move, so be it. But if it finds a move as good as the previously best seen,
# take the one that will reach the end the fastest
alpha = quality[0]
movement = move
worlength = quality[2] #set the shortest game length seen to the current one
elif player == x: #'X' minimizes the score and wants to increase the length of the game
if (quality[0] < beta) or ((quality[0] <= beta) and (quality[2] > optlength)):
# If it finds a better move, so be it. But if it finds a move as good as the previously best seen,
# take the one that will reach the end the longest
beta = quality[0]
movement = move
optlength = quality[2] #set the longest game length seen to the current one
if alpha >= beta:
# prunes
break
if player == o:
return ([alpha, movement,worlength])
else:
return ([beta, movement,optlength])
I am implementing Checkers game using Minimax algorithm and Python. There are two players - both are computers. I was looking for a similar problem's solution but I could not find any and I have been struggling with it for few days. My entry point is this function:
def run_game(board):
players = board.players
is_move_possible = True
move = 0
while is_move_possible:
is_move_possible = move_piece_minimax(board, players[move % 2])
move += 1
It starts the game and calls the next function which is supposed to do the best move, basing on MiniMax algorithm, for the first player. After that first move, it calls this function for the second player and this loop will end once the game is won by one of the players. This function looks as following:
def move_piece_minimax(board, player):
best_move = minimax(copy.deepcopy(board), player, 0)
if best_move.score == +infinity or best_move.score == -infinity:
return False
move_single_piece(board.fields, player, best_move)
return True
The first line calls the MiniMax algorithm, which I will describe later, and is supposed to find the best possible move for the player. I am passing a deep copy of whole board here, as I do not want the original one to be edited during execution of MiniMax algorithm. The condition checks for a win condition so if either maximizing player wins or minimizing one. If none of them won, best_move is performed. Moving to the main problem here, I implemented MiniMax algorithm as following:
def minimax(board, player, depth):
best_move = Move(-1, -1, -infinity if player.name == PLAYER_NAMES['P1'] else +infinity)
if depth == MAX_SEARCH_DEPTH or game_over(board):
score = evaluate(board)
return Move(-1, -1, score)
for correct_move in get_all_correct_moves(player, board.fields):
x, y, piece = correct_move.x, correct_move.y, correct_move.piece
move_single_piece(board.fields, player, correct_move)
player_to_move = get_player_to_move(board, player)
move = minimax(board, player_to_move, depth + 1) # <--- here is a recursion
move.x = x
move.y = y
move.piece = piece
if player.name == PLAYER_NAMES['P1']:
if move.score > best_move.score:
best_move = move # max value
else:
if move.score < best_move.score:
best_move = move # min value
return best_move
I decided that player 'P1' is a maximizing player and player 'P2' is a minimizing one. Starting from the first line, best_move variable holds a reference to a Move object which has the following fields:
class Move:
def __init__(self, x, y, score, piece=None):
self.x = x
self.y = y
self.score = score
self.piece = piece
I am initializing best_move.score to -Infinity in case of the maximizing player and to +Infinity otherwise.
The first condition checks if depth reached the maximal level(for testing purposes it is set to 2) or the game is over. If yes, it evaluates current board's situation and returns a Move object holding current board's score. Otherwise, my algorithm looks for all legal/correct moves for the player and performs the first one.
After performing it, this function is called in a recursive manner but with incremented depth and changed moving player. The function runs again with changing parameters until the first if condition executes.
Once the execution goes to that branch, the board's evaluation score is returned and later, in a for loop after recursive call, coordinates x, y and a piece, which was moved, are assigned to a Move object.
Last conditions check if the new score is a better score for that specific player. If this is a maximizing player, so in my case P1, it checks if new score is higher that the previous one. In the case of minimizing player, the algorithm looks for the lowest score.
After performing all correct moves for that player, my algorithm should return one best_move.
Expected result
Single object of a Move class with coordinates x and y, evaluated board's score, which is +Infinity/-Infinity only in a case of win for one of the players, and an object of Piece class which will be moved to [x, y] coordinates.
Actual result
Single object of Move class with coordinates x and y, evaluated board's score which is equal to +Infinity after first call of MiniMax function. None of pieces changed its position so the game is not over yet. However, score is +Infinity so function move_piece_minimax() will return False - meaning no more moves are possible. Therefore, my program will stop execution with no changes on the board. Here is the screenshot of initial and final board's states - nothing is changed during exectuion as the first call returns +Infinity.
My question is, what I missed during implementation of MiniMax algorithm? Did I make any mistake? I am also open to any code improvements or suggestions. If any additional functions will be needed for you to understand my implementation, I will provide them. Thank you!
In the minimax function, you should do either of the following
1. Make a copy of your board before placing pieces
2. Remove the placed piece after recursively calling your minimax function
Otherwise your board will be getting filled with pieces with recursion and you'll receive an error stating that there's no moves left. Minimax is meant to do in-depth searching by placing pieces, so you should implement a method so it doesn't modify your original board.
I'm working on the safe queens problem and I'm curious if there is a constant time solution to the question.
Safe Queens
In chess the queen is the most powerful piece, as it can move any number of unoccupied squares in a straight line along columns (called "files"), rows (called "ranks"), and diagonals. It is, however, possible to place 8 queens on a board such that none are threatening another.
In this challenge we read an input of 8 positions in standard algebraic notation (files "a" to "h" and ranks "1" to "8"). Our goal is to determine, using the safe_queens function, if all of the queens are safe from each other – that is, none of them should share a file, rank, or diagonal. If they are all safe, the function should print 'YES', otherwise it should print 'NO'.
Example:
Input:
a5
b3
c1
d7
e2
f8
g6
h4
(example input positions shown on board below)
Output:
YES
This example outputs 'YES' because no queens share a file, rank, or diagonal. If the a5 were an a4 it would output 'NO' because the leftmost two queens would be on the same diagonal.
I believe that you could transform the solution be into constant time by keeping track of visited squares and checking for inference for the diagonals, but I'm not 100% sure.
def safe_queens():
positions = [input() for _ in range(8)]
# if there isn't exactly one queen per file and per rank then return 'NO'
if (len({i[0] for i in positions}) != 8) or (
len({i[1] for i in positions}) != 8):
return 'NO'
# to check diagonals compare difference between each queen combinations
# rank values and file values, adjust for both diagonal directions by
# using abs()
for p in range(8):
for q in range(p+1, 8):
if (abs(ord(positions[p][0]) - ord(positions[q][0]))) == (
abs(int(positions[p][1]) - int(positions[q][1]))):
return 'NO'
return 'YES'
Link to colab notebook with the problem description and my soluion: https://colab.research.google.com/drive/1sJN6KnQ0yN-E_Uj6fAgQCNdOO7rcif8G
The solution above is O(n^2), but I'm looking for a constant time solution. Thanks!
Just do the same for the diagonals as you do for the files and ranks: make a set of all diagonal numbers and check for the length of the set. You can number the diagonals this way:
diagonals1 = {ord(position[0]) - int(position[1]) for position in positions}
if len(diagonals1) < 8:
return 'NO'
diagonals2 = {ord(position[0]) + int(position[1]) for position in positions}
if len(diagonals2) < 8:
return 'NO'
Start with a one dimensional space of length m, where m = 2 * n + 1. Take a step either to the left or to the right at random, with equal probability. Continue taking random steps until you go off one edge of the space, for which I'm using while 0 <= position < m.
We have to write a program that executes the random walk. We have to create a 1D space using size n = 5 and place the marker in the middle. Every step, move it either to the left or to the right using the random number generator. There should be an equal probability that it moves in either direction.
I have an idea for the plan but do not know how to write it in python:
Initialize n = 1, m = 2n + 1, and j = n + 1.
Loop until j = 0 or j = m + 1 as shown. At each step:
Move j left or right at random.
Display the current state of the walk, as shown.
Make another variable to count the total number of steps.
Initialize this variable to zero before the loop.
However j moves always increase the step count.
After the loop ends, report the total steps.
1 - Start with a list initialized with 5 items (maybe None?)
2 - place the walker at index 2
3 - randomly chose a direction (-1 or + 1)
4 - move the walker in the chosen direction
5 - maybe print the space and mark the location of the walker
6 - repeat at step 3 as many times as needed
Given the dimensions of a rectangle,(m,n), made up of unit squares, output the number of unit squares the diagonal of the rectangle touches- that includes borders and vertices.
My algorithm approaches this by cycling through all the unit squares(under assumption that can draw our diagonal from (0,0) to (m,n)
My algorithm solves 9 of 10 tests, but is too inefficient to solve the tenth test in given time.
I"m uopen to all efficiency suggestions, but in the name of asking a specific question... I seem to be having a disconnect in my own logic concerning adding a break statement, to cut some steps out of the process. My thinking is, this shouldn't matter, but it does affect the result, and I haven't been able to figure out why.
So, can someone help me understand how to insert a break that doesn't affect the output.
Or how to eliminate a loop. I"m currently using nested loops.
So, yeah, I think my problems are algorithmic rather than syntax.
def countBlackCells(m, n):
counter=0
y=[0,0]
testV=0
for i in xrange(n): #loop over m/x first
y[0]=float(m)/n*i
y[1]=float(m)/n*(i+1)
#print(str(y))
for j in xrange(m): #loop over every n/y for each x
if((y[0]<=(j+1) and y[0]>=j) or (y[1]>=(j) and y[1]<=j+1)):#is min of line in range inside teh box? is max of line?
counter+=1
#testV += 1
else: pass # break# thinking that once you are beyond the line in either direction, your not coming back to it by ranging up m anymore. THAT DOESN"T SEEM TO BE THE CASE
#tried adding a flag (testV), so that inner loop would only break if line was found and then lost again, still didn't count ALL boxes. There's something I'm not understanding here.
return counter
Some sample, input/output
Input:
n: 3
m: 4
Output:
6
Input:
n: 3
m: 3
Output:
7
Input:
n: 33
m: 44
Output:
86
Find G - the greatest common divisor of m and n.
If G > 1 then diagonal intersects G-1 inner vertices, touching (not intersecting) 2*(G-1) cells.
And between these inner vertices there are G sub-rectangles with mutually prime sides M x N (m/G x n/G)
Now consider case of mutually prime M and N. Diagonal of such rectangle does not intersect any vertex except for starting and ending. But it must intersect M vertical lines and N horizontal lines, and at every intersection diagonal enters into the new cell, so it intersects M + N - 1 cells (subtract 1 to account for the first corner where both vertical and horizontal lines are met together)
So use these clues and deduce final solution.
I used math.gcd() to solve the problem in python.
def countBlackCells(n, m):
return m+n+math.gcd(m,n)-2