Backtracking problem in Knights tour algorithm - python-3.x

**Edit: I managed to find solution for my problem so i put the correct code in place. Everything works perfect now.
I'm trying to complete the last stage of a project in Jetbrains Academy but I can't make my approach to the problem work:
It gives the user the solution for the knights tour in chess if it exists by taking their inputs. I know that there are working solutions out there, but I would like to make it work with the approach I have taken.
I can't make it work. Although, it works fine when the player plays with and I had cut the extra lines.
Example:
Input: A 4x3 board, and 1 3 (column/row)
My implementation gives 2 possible moves [(3, 2), (2, 1)]. It then continues for the possible values of 2 1 and it goes all the way down until it has no other solutions. Then it stops. Those inputs works because they dont require further search in the inside possible valid move. It doesn't check multiple paths that exist at the deeper level.
I don't understand why this happens. Where is the problem in the code?
Initialization of the board (there are extra variables because of the previous steps):
# Write your code here
from copy import deepcopy
import sys
sys.setrecursionlimit(2000)
# With that recursion limit you can test boards up to 40x40 instantly.
# Without this you can test easily up to 30 x 30 any value
# and up to 35x35 any value except edge columns / rows ex 1 4 / 4 1.
class ChessBoard:
def __init__(self, dimensions: str):
self.game_over = False
self.valid_grid = True
self.dimensions = self.board_validation(dimensions.strip().split())
if self.valid_grid:
self.x = self.dimensions[0]
self.y = self.dimensions[1]
self.cell_length = len(str(self.x * self.y)) # Cell format helper for spaces
self.board = [[(x, y) for x in range(1, self.x + 1)] for y in range(self.y, 0, -1)]
self.visual = [["_" * self.cell_length for _ in range(1, self.x + 1)] for _ in range(self.y, 0, -1)]
self.moves_visit = {key: False for i in range(len(self.board)) for key in self.board[i]}
# We need duplicates to reverse the state of the board if we want:
# to continue the game - try other initial move for the valid movements pool
self.board_duplicate = deepcopy(self.board)
self.visual_duplicate = deepcopy(self.visual)
self.moves_visit_duplicate = deepcopy(self.moves_visit)
self.x_boundaries = {i for i in range(1, self.x + 1)}
self.y_boundaries = {i for i in range(1, self.y + 1)}
self.valid_moves = [] # Helper for getting instant valid_moves for the player.
self.is_first_move = True # With that we make it easier to differentiate the first move from other inputs.
self.movements_made = 0 # Move counter direct connection with the AI solution finder and the AI move
self.board_dimensions = self.x * self.y
self.has_solution = False
self.initial_move_for_ai = None
self.first_valid_move = set({})
self.moves_placed = set({}) # Fast comparison to check if the ai_play function will return from recursion
self.total_moves = 0
self.find_solution_mode = False
def set_first(self):
if self.is_first_move:
self.is_first_move = False
def board_validation(self, board_dimensions):
if len(board_dimensions) != 2:
self.valid_grid = False
return False
try:
x = int(board_dimensions[0])
y = int(board_dimensions[1])
if x <= 0 or y <= 0:
self.valid_grid = False
return False
self.valid_grid = True
return x, y
except ValueError:
self.valid_grid = False
return False
def move_validation(self, move_to_validate):
if isinstance(move_to_validate, tuple) and move_to_validate[0] in self.x_boundaries and \
move_to_validate[1] in self.y_boundaries:
return move_to_validate
values = move_to_validate.split()
if len(values) != 2:
return False
try:
column = int(values[0])
row = int(values[1])
movement = (column, row)
if column not in self.x_boundaries or row not in self.y_boundaries:
return False
if not self.is_first_move and self.moves_visit[movement]:
return False
if not self.is_first_move and movement not in self.valid_moves:
return False
return movement
except ValueError:
return False
# Knight movement. X for player - Movement number for AI
def knight_move(self, movement, solution_mode=False):
if self.movements_made == 0:
self.set_first()
movement = self.move_validation(movement)
for m in range(len(self.board)):
if movement in self.board[m]:
index_of_move = self.board[m].index(movement)
if not self.moves_visit[movement]:
if not solution_mode:
self.visual[m][index_of_move] = " " * (self.cell_length - 1) + "X"
else:
move = "" * (self.cell_length - 3) + str(self.movements_made + 1)
self.visual[m][index_of_move] = move.rjust(self.cell_length)
self.possible_moves(movement)
self.movements_made += 1
self.moves_visit[movement] = True
return movement
return False
# Calculating exact moves to chose from. We return index based values based on visual repr
# and also player based values (2, 1), (3, 2) etc.. columns / rows
def move_calculation(self, movement_to_calc):
movement = movement_to_calc
value_to_process = [(-2, -1), (-2, 1), (-1, 2), (1, 2), (2, 1), (2, -1), (-1, -2), (1, -2)]
possible_calculations = [(movement[0] + value_to_process[i][0], movement[1] +
value_to_process[i][1]) for i in range(len(value_to_process))]
final_possible_indexes = [(r, self.board[r].index(j)) for r in range(len(self.board))
for j in possible_calculations if j in self.board[r] and not self.moves_visit[j]]
final_possible_indexes_translation = [self.board[i][j] for i, j in final_possible_indexes]
return final_possible_indexes, final_possible_indexes_translation
# Function that is used to calculate how many moves we have for the valid placements on board
def depth_calc(self, valid_move):
x, y = self.move_calculation(valid_move)
return len(x)
# Similar to move_calculation but this one is used to register primary valid moves for player on board
# And also to check the state of players game and outputting the depth values for the valid moves.
def possible_moves(self, main_move):
try:
moves, moves_translation = self.move_calculation(main_move)
combined_index_translation = set(zip(moves, moves_translation))
except TypeError:
return False
if not self.find_solution_mode:
for i, j in combined_index_translation:
col, row = i[0], i[1]
if not self.moves_visit[j]:
self.visual[col][row] = f'{" " * (self.cell_length - 1)}{self.depth_calc(j) - 1}'
self.valid_moves = moves_translation
print(self.__str__())
self.board_updater()
self.check_end()
return moves_translation
# Board update based on the moves_visit state True/False (if we visited or not)
def board_updater(self):
for i in range(len(self.board)):
for j in range(len(self.board[i])):
move_to_check = self.board[i][j]
if self.moves_visit[move_to_check]:
self.visual[i][j] = f'{" " * (self.cell_length - 1)}*'
else:
self.visual[i][j] = f'{" " * (self.cell_length - 1)}_'
# Getting all primary valid moves to work with
def get_valid_moves(self, from_move):
valid = self.possible_moves(from_move)
if valid:
return valid
else:
return False
# Key function for the ai_play function. It gives us the valid move with the minimum depth.
def lower_move(self, valid_values):
lengths = {i: self.depth_calc(i) for i in valid_values}
min_depth = min(lengths, key=lengths.get)
return min_depth
# Recursive function that is giving us the solution for the inputted move
def ai_play(self, move, pool, n):
if not self.moves_placed:
self.moves_placed.add(move)
if n == 1:
self.first_valid_move.add(self.lower_move(pool))
if len(self.moves_placed) == self.board_dimensions:
self.has_solution = True
return True
if self.total_moves >= self.board_dimensions ** 4:
return False
self.total_moves += 1
if pool:
best_move = self.lower_move(pool)
ai_move = self.knight_move(best_move, solution_mode=True)
self.moves_placed.add(ai_move)
if ai_move:
possible_moves = self.get_valid_moves(ai_move)
if self.ai_play(ai_move, possible_moves, n + 1):
return True
else:
self.ai_play_reset()
first_move = self.knight_reset()
self.moves_placed.add(first_move)
first_pool = self.get_valid_moves(first_move)
self.first_valid_move.add(first_pool[0])
new_pool = set(first_pool).difference(self.first_valid_move)
try:
self.ai_play(first_move, list(new_pool), 1)
except RecursionError:
self.ai_play_reset()
return False
except IndexError:
print("We tested all initial valid moves and failed.\nChoose one cell higher column and try again")
return False
# Resets the important variables of the board. Crucial if ai_play doesn't find a solution with first try.
def ai_play_reset(self):
self.moves_placed.clear()
self.board = deepcopy(self.board_duplicate)
self.visual = deepcopy(self.visual_duplicate)
self.moves_visit = deepcopy(self.moves_visit_duplicate)
self.movements_made = 0
self.has_solution = False
self.first_valid_move.clear()
# Checks the state of the player's game.
def check_end(self):
if not self.valid_moves:
self.game_over = True
if self.movements_made + 1 == self.board_dimensions:
self.has_solution = True
print("What a great tour! Congratulations!")
return False
else:
print(f"No more possible moves!\nYour knight visited {self.movements_made + 1} squares!")
return False
# Resets the Knight position to the initial for the ai_play function
def knight_reset(self):
main_move = self.knight_move(self.initial_move_for_ai, solution_mode=True)
return main_move
# Helper function that manages the outputs of the ai_play function regarding if player want a solution or not
def ai_solution_test(self, move, for_player=False):
if self.board_dimensions in {16, 4, 9}:
print("No solution exists!")
return False
self.initial_move_for_ai = move
self.find_solution_mode = True
movement = self.knight_move(move, solution_mode=True)
self.set_first()
self.initial_move_for_ai = movement
valid_moves = self.get_valid_moves(movement)
while True:
self.ai_play(movement, valid_moves, 1)
if self.has_solution:
if not for_player:
print("\nHere's the solution!")
print(self.__str__())
break
else:
break
self.find_solution_mode = False
self.ai_play_reset()
return True
def __str__(self):
visualization = [" ".rjust(self.cell_length - 1) + "-" * (self.x * (self.cell_length + 1) + 3)]
for i in range(len(self.visual)):
main_chess = " ".join([str(len(self.visual) - i).rjust(self.cell_length - 1) + "|", *self.visual[i], "|"])
visualization.append(main_chess)
visualization.append(" " * (self.cell_length - 1) + "-" * (self.x * (self.cell_length + 1) + 3))
visualization.append(
" ".rjust(self.cell_length + 2) + " ".join([f"{i}".center(self.cell_length) for i in range(1, self.x + 1)]))
return "\n".join(visualization)
def main():
while True:
board = ChessBoard(input("Enter your board's dimensions: "))
if not board.valid_grid:
print("Invalid dimensions!", end=" ")
continue
while True:
knight_move = board.move_validation(input("Enter the knight's starting position: "))
if not knight_move:
print("Invalid move!", end=" ")
continue
else:
break
while True:
puzzle_try = input("Do you want to try the puzzle? (y/n): ")
if puzzle_try not in {"y", "n"}:
print("Invalid input!", end=" ")
continue
else:
break
if puzzle_try == "y":
check_if_solution = board.ai_solution_test(knight_move, for_player=True)
if check_if_solution:
board.knight_move(knight_move)
while not board.game_over:
next_move = board.knight_move(input("Enter your next move: "))
if next_move:
board.board_updater()
else:
print("Invalid move!", end=" ")
continue
if board.game_over:
break
else:
break
else:
board.ai_solution_test(knight_move)
break
if __name__ == "__main__":
main()

Related

Saving a value, removing it and then running again with the rest of a list

Problem Description
I am firstly I am having a list of values like:
(1,2,3,1,3,2,2,2,1)
What I want to achieve is :
Find the most common one (working).
Delete it from the list (working).
Repeat the same thing for the remaining values in the list.
For instance
Taking this Tuple:
(1,2,3,1,3,2,2,2,1)
Store that 2 is the most common int, then
(1,3,3,1,1,)
Store that 1 is the second most common int, then
(3,3)
Store that 2 is the third most common int, then
My source Code
NOTE lets say that self.final_votes would be (1,2,3,1,3,2,2,2,1)
def erase_common(self):
possitions = []
counter = 0
common = self.final_votes[0]
for i in self.final_votes:
cand_freq = self.final_votes.count(i)
if(cand_freq> counter):
counter = cand_freq
common = i
possitions.append(common)
while common in self.final_votes:
self.final_votes.remove(common)
this is the whole code
class voting_system:
def __init__(self):
#self.tutor_group = self.get_tutor_group()
self.stud_num = self.get_stud_num()
self.cand_num = self.get_cand_num()
self.cand_name = self.get_cand_name()
self.final_votes = self.ind_votes()
self.erase_common()
# def get_tutor_group(self):
# tutor_groups = ("7A","7B","7C","7D","7E","7F","8A","8B","8C","8D","8E","8F","9A","9B","9C","9D","9E","9E",
# "9F","10A","10B","10C","10D","10E","10F","11A","11B","11C","11D","11E","11F")
#
# flag = True
# while flag == True:
# try:
# self.tutor_group = input("please enter the tutor group: ")
# if self.tutor_group not in tutor_groups:
# raise ValueError("Tutor group entred doesn't exist")
# except ValueError as error:
# print(error)
# flag = True
#
# else:
# flag = False
# return self.tutor_group
def get_stud_num(self):
# flag = True
# while flag == True:
# try:
# self.stud_num = int(input("Please enter number of students: "))
# if self.stud_num<0 or self.stud_num>35:
# raise ValueError("NUMBER OF STUDENTS INVALID")
# except ValueError as error:
# print(error)
# flag = True
#
# else:
# flag = False
self.stud_num = 7
return self.stud_num
def get_cand_num(self):
# flag = True
# while flag == True:
# try:
# self.cand_num = int(input("Please enter number of candidates: "))
# if self.cand_num>4:
# raise ValueError("NUMBER OF CANDIDATES INVALID")
# except ValueError as error:
# print(error)
# flag = True
#
# else:
# flag = False
self.cand_num = 3
return self.cand_num
def get_cand_name(self):
self.cand_name = []
for i in range(self.cand_num):
candidate = input("Enter candidate name: ")
self.cand_name.append(candidate)
return self.cand_name
def print_cands(self):
for i in range(len(self.cand_name)):
print(i+1, " : ", self.cand_name[i])
def ind_votes(self):
self.final_votes = []
self.print_cands()
abs_vote = 0
flag = True
while flag == True:
try:
for i in range(self.stud_num):
vote = input("To vote type 'V' and the candidates number, to abstent type 'A': ")
if vote.isalpha() == True:
abs_vote +=1
else:
self.final_votes.append(vote[1])
if vote[1] > str(self.cand_num):
raise ValueError("VOTED CANDIDATE NONEXISTENT")
except ValueError as error:
print(error)
flag = True
else:
flag = False
return self.final_votes
return abs_vote
def erase_common(self):
possitions = []
counter = 0
common = self.final_votes[0]
for i in self.final_votes:
cand_freq = self.final_votes.count(i)
if(cand_freq> counter):
counter = cand_freq
common = i
possitions.append(common)
while common in self.final_votes:
self.final_votes.remove(common)
run = voting_system()
any suggestions would be appreciated:)
Easier and probably the best:
User Python Counter from Collection.
from collections import Counter
final_votes = [1, 2, 3, 1, 3, 2, 2, 2, 1]
def erase_common(votes):
count_votes = Counter(votes)
return count_votes.most_common()
print(erase_common(final_votes)) # [(2, 4), (1, 3), (3, 2)]
votes = erase_common(final_votes)
for idx, votes in enumerate(votes):
print(f'{idx+1}) Vote {votes[0]} Total = {votes[1]}')
OUTPUT
1) Vote 2 Total = 4
2) Vote 1 Total = 3
3) Vote 3 Total = 2
Using groupby
#AlexMartelli has a really good answer regarding find most common element on list --> Find the most common element in a list
So you can implement this by doing this:
from itertools import groupby
final_votes = [1, 2, 3, 1, 3, 2, 2, 2, 1]
def most_common_oneliner(L):
grupped = groupby(sorted(L))
result = {group: len([*scores]) for group, scores in grupped}
final_sorted_votes = []
while result:
max = [(0, 0)]
for score, res in result.items():
if max[0][1] < res:
max[0] = (score, res)
del result[max[0][0]]
final_sorted_votes += max
return final_sorted_votes
result = most_common_oneliner(final_votes)
for idx, votes in enumerate(result):
print(f'{idx+1}) Vote {votes[0]} Total = {votes[1]}')
Documentation
Counter
Counter Implementation
most_common function inplementation
groupby
how-do-i-use-itertools-groupby Stack Overflow
geeksforgeeks.org | python-find-most-frequent-element-in-a-list

Deep Q learning-Training issue

A snake game was created using Pygame and I tried to solve it using an AI. Initially I didn't increase the body length to check if the snake head moves towards the food. Grid size is 5*5. DDQN network was used. Most of time the head moves towards the wall or gets struck in a continuous loop.The maximum score attained was 4 even if I train it for 5000 episodes.
State: It is an array of size 16. The first 8 values has the distance between the head and wall at 8 directions(left , left top, top, right top, right , right bottom, bottom, left bottom). Next 8 values has the distance between head and food at 8 directions. All the values are in the range 0 to 1. 1 means the object is near and 0 means it is very far.
Action : There are 3 actions 0,1,2. 0- Head moves in same direction. 1- Head turns left. 2- Head turns right.
Reward: Reward of +50 if it collects the food and reward of -200 if it touches the wall.
I am not able to understand why my neural network learns in the wrong way. Please do help me solve this issue. I have attached the code here.
Code:
import pygame
pygame.font.init()
import time
import random
import numpy as np
from math import hypot
from collections import deque
from keras.models import Sequential
from keras.layers import Dense
from keras.optimizers import Adam
import tensorflow as tf
import os
seeds = 1001
os.environ['PYTHONHASHSEED']=str(seeds)
np.random.seed(seeds)
random.seed(seeds)
tf.random.set_seed(seeds)
batch = 16
class Food:
def __init__(self,width,n):
self.width = width
self.n = n
self.size = width //n
self.blocks = self.blocks_total()
def blocks_total(self):# Total available positions
x = y = 0
s=[]
for i in range(self.n):
for j in range(self.n):
s.append([x,y])
y += self.size
x += self.size
y = 0
return s
def food_pos(self,s):# Random food position
food_blocks = self.blocks_total()
try:
for i in self.blocks:
for j in s:
if i[0] == j[0] and i[1] == j[1]:
food_blocks.remove([j[0],j[1]])
break
a = random.choice(food_blocks)
return a
except:
return 0.1,0.1
class Agent:
def __init__(self,width,n,state_size,action_size=3,gamma = 0.98):
self.width = width
self.n = n
self.size = width //n
self.state_size = state_size
self.action_size = action_size
self.gamma = gamma
self.epsilon = 1
self.epsilon_min = 0
self.epsilon_decay = 0.99
self.memory = deque(maxlen=5000)
self.model = self.build_model()
self.train_model = self.build_model()
def reshape(self,state):# Reshaping state for input in nueral network
return np.reshape(state,[1,state.shape[0]])
def build_model(self):# Nueral network
model = Sequential()
model.add(Dense(16,input_shape=(self.state_size,),activation='relu'))
model.add(Dense(12,activation='relu'))
model.add(Dense(12,activation='relu'))
model.add(Dense(3,activation='softmax'))
model.compile(loss='mse',optimizer=Adam(0.0001))
return model
def get_action(self,state):
if np.random.rand() <= self.epsilon:
return random.randint(0,2)
a = self.reshape(state)
p = self.model.predict(a)
return np.argmax(p[0])
def remember(self,state,action,reward,new_state,done): # Saving in memory
self.memory.append((state,action,reward,new_state,done))
def replay(self): # Training of nueral network
minibatch = random.sample(self.memory,batch)
for state,action,reward,new_state,done in minibatch:
target = reward
state = self.reshape(state)
new_state = self.reshape(new_state)
if not done:
target = reward +(self.gamma*(np.max(self.train_model.predict(new_state)[0])))
target_f = self.model.predict(state)
target_f[0][action] = target
self.model.fit(state,target_f,epochs =1, verbose = 0)
if self.epsilon > self.epsilon_min:
self.epsilon *= self.epsilon_decay
def save_model(self):
self.model.save_weights('nn1.h5')
def load_model(self):
self.train_model.load_weights('nn1.h5')
class Game:
def __init__(self,width=500,n=5):
self.width = width
self.n = n
self.size = width//n
self.display = pygame.display.set_mode((width,width))
pygame.display.set_caption('A')
self.food = Food(width,n)
self.agent = Agent(width, n, state_size=16)
self.game_over = False
self.dirc = 'r'# starting direction of snake
self.snake_list = []
self.snake_length = 1
self.wall_touch = False
def display_player(self,disp,s_list,dirc):# displaying the snake head and its eyes
l = len(s_list)
if l > self.snake_length:
del s_list[0]
l -=1
for idx,i in enumerate(s_list):
if idx == l-1:
pygame.draw.rect(disp,(255,255,255),(i[0],i[1],self.size-1,self.size-1))
else:
pygame.draw.rect(disp,(255,165,0),(i[0],i[1],self.size-1,self.size-1))
a = s_list.copy()
x,y = a.pop()
rad = 10
if dirc == 'u':
pygame.draw.circle(disp,(0,0,0),(x+50,y+30),rad)
elif dirc == 'd':
pygame.draw.circle(disp,(0,0,0),(x+50,y+70),rad)
elif dirc == 'r':
pygame.draw.circle(disp,(0,0,0),(x+70,y+49),rad)
elif dirc == 'l':
pygame.draw.circle(disp,(0,0,0),(x+30,y+49),rad)
def move(self,dirc,s_list):# Constantly moving the snake on that particular direction
x,y = s_list.pop()
if dirc == 'l':
x -= self.size
if x <0:
x = 0
self.wall_touch = True
elif dirc == 'r':
x += self.size
if x > self.width - self.size:
x = self.width - self.size
self.wall_touch = True
elif dirc == 'u':
y -= self.size
if y <0:
y = 0
self.wall_touch = True
elif dirc == 'd':
y += self.size
if y > self.width - self.size:
y = self.width - self.size
self.wall_touch = True
self.snake_list.append([x,y])
def check_food_collect(self,fx,fy,s_list):# Check if head position and food position are same
x,y =s_list.pop()
if x == fx and y == fy:
return True
return False
def display_msg(self,msg,font='freesansbold.ttf',size=15,color=(255,255,255),loc=(410,15)):
mymsg = pygame.font.Font(font,size).render(msg,True,color)
self.display.blit(mymsg,loc)
def get_direction(self,change):# Changing the direction of snake based on the current direction
if self.dirc == 'r':
if change == 'r':
self.dirc= 'd'
elif change == 'l':
self.dirc= 'u'
elif self.dirc == 'l':
if change == 'r':
self.dirc= 'u'
elif change == 'l':
self.dirc= 'd'
elif self.dirc == 'd' :
if change == 'r':
self.dirc= 'l'
elif change == 'l':
self.dirc= 'r'
elif self.dirc == 'u':
if change == 'r':
self.dirc= 'r'
elif change == 'l':
self.dirc= 'l'
def near_wall(self,n,s_list): # Distance of nearby wall
a,b = n
x,y = s_list.pop()
i =0
while True:
xx = x +(self.size * i * a)
yy = y +(self.size * i * b)
dis = abs(xx-x)/self.size,abs(yy-y)/self.size
i +=1
if xx <0 or yy <0 or xx > self.width - self.size or yy > self.width - self.size:
return 1/ hypot(dis[0],dis[1])
def get_wall_dis(self,s_list): # Wall distance at 8 direction
j = [[-1,0],[-1,-1],[0,-1],[1,-1],[1,0],[1,1],[0,1],[-1,1]]
s = []
for i in j:
s.append(self.near_wall(i, s_list.copy()))
s = np.asarray(s)
return s
def near_food(self,fx,fy,n,s_list):# Head looks at 8 direction for the food
a,b = n
x,y = s_list.pop()
i =0
while True:
xx = x +(self.size * i * a)
yy = y +(self.size * i * b)
dis = abs(x-fx)/self.size,abs(fy-y)/self.size
i +=1
if xx <0 or yy <0 or xx > self.width - self.size or yy > self.width - self.size:
return 0
else:
if xx == fx and yy == fy:
if dis[0] ==0 and dis[1] ==0:
return 0
return 1/hypot(dis[0],dis[1])
def get_state(self,fx,fy,s_list,w): # Array of size 16
j = [[-1,0],[-1,-1],[0,-1],[1,-1],[1,0],[1,1],[0,1],[-1,1]]
s = []
for i in j:
s.append(self.near_food(fx,fy,i, s_list.copy()))
s = np.asarray(s)
a = np.append(w,s)
return a
def reset(self): # Initialising the value when a new game is started
self.agent.load_model()
self.game_over = False
self.wall_touch = False
self.dirc = 'r'
self.snake_length = 1
self.snake_list = []
def startgame(self,e):
sx,sy = 0,0 # Starting position of snake
self.snake_list.append([sx,sy])
fx,fy = 200,200 # Initial position of food
step = 0
action = 0
score = 0
change = None
wall = self.get_wall_dis(self.snake_list.copy())
state = self.get_state(fx, fy, self.snake_list.copy(), wall)
reward = 0
t = 0
save = False
j = 0
while not self.game_over:
j +=1
if j >500: # If snake struck in continuous loop
print('Ended')
break
action = 0
action = self.agent.get_action(state)
if action ==1:
change = 'l'
step = 1
elif action ==2:
change = 'r'
step = 1
else:
action = 0
change = None
if step == 0:
step = 1
else:
t +=1
save = True
self.get_direction(change)
change = None
self.move(self.dirc,self.snake_list.copy())
wall = self.get_wall_dis(self.snake_list.copy())
new_state = self.get_state(fx, fy, self.snake_list.copy(), wall)
if self.wall_touch:
reward = -200
print('Walled')
self.agent.remember(state, action, reward, new_state, True)
break
food_collect = self.check_food_collect(fx, fy, self.snake_list.copy())
if food_collect:
reward += 50
self.agent.remember(state, action, reward, new_state, False)
save = False
score +=1
step = 0
t = 0
reward = 0
fx,fy = self.food.food_pos(self.snake_list.copy())
if fx == 0.1 and fy ==0.1:
print('COMPLETED')
pygame.quit()
if save:
save = False
self.agent.remember(state, action, reward, new_state, False)
self.display.fill((0,0,0))
self.display_msg('Score :'+str(score))
pygame.draw.rect(self.display,(0,255,0),(fx,fy,self.size-1,self.size-1))
self.display_player(self.display, self.snake_list,self.dirc)
pygame.display.update()
#time.sleep(1)
state = new_state
if e > 2500:
time.sleep(0.1)
print('E : {} , Epsilon :{:.2} , Score : {}'.format(e,np.float32(self.agent.epsilon),score))
if e%10 ==0:
self.agent.save_model()
if len(self.agent.memory) > batch:
self.agent.replay()
game = Game()
for e in range(10000):
game.startgame(e)
game.reset()
pygame.quit()
A few things you could try:
The way you have defined your state looks a little complicated. Won't top and bottom give the same information, one being negative of the other? Also if your snake head is at (1,1) and your fruit at (3,4), well then the fruit won't show up in the state at all. There will be very limited times when the snake agent can actually see the fruit. Maybe you can try defining the state another way?
In RL, things go south very frequently so it often makes sense to start with basic agents and basic games and move up the ladder. Try using the same agent for a simple openai gym environment like mountaincar, to check if the agent class works as intended.

how to create an impossible sudoku detector?

I'm making a sudoku solver on pygame, using backtracking. It's working, both the backtracking and the gui, however, when the user tries to solve an impossible puzzle, the game freezes.
I want to make a modification to my algorithm that, when the puzzle is impossible, the algorithm detects it, warning the user and reseting the board.
For now, this is the main code for the board class.
class Board:
solvedBoard = [[0 for i in range(9)] for j in range(9)]
gameRunning = True
def __init__(self):
self.tabuleiro = [[0 for i in range(9)] for j in range(9)]
def reset(self):
self.tabuleiro = [[0 for i in range(9)] for j in range(9)]
self.gameRunning = True
def show(self):
for i in self.tabuleiro:
for j in i:
print(j, end = ' ')
print('')
def setCell(self, linha, coluna, valor):
self.tabuleiro[linha][coluna] = valor
def isFull(self):
for i in range(9):
for j in range(9):
if self.tabuleiro[i][j] == 0:
return False
return True
def numberPossibilities(self, linha, coluna):
possible = [1 for i in range(10)]
#horizontal check
for col in range(9):
if self.tabuleiro[linha][col]:
possible[self.tabuleiro[linha][col]] = 0
#vertical check
for line in range(9):
if self.tabuleiro[line][coluna]:
possible[self.tabuleiro[line][coluna]] = 0
#mini square check
linhaSquare = (linha // 3) * 3
colunaSquare = (coluna // 3) * 3
for l in range(linhaSquare, linhaSquare + 3):
for c in range(colunaSquare, colunaSquare + 3):
if self.tabuleiro[l][c]:
possible[self.tabuleiro[l][c]] = 0
toTry = []
for k in range(1, 10):
if possible[k]:
toTry.append(k)
return toTry
def solve(self):
self.show()
print('')
if self.isFull():
for i in range(9):
for j in range(9):
self.solvedBoard[i][j] = self.tabuleiro[i][j]
self.gameRunning = False
self.tabuleiro = self.solvedBoard
elif self.gameRunning:
found = False
linha = 0
coluna = 0
for i in range(9):
for j in range(9):
if (not self.tabuleiro[i][j]):
linha = i
coluna = j
found = True
break
if found:
break
numbers = self.numberPossibilities(linha, coluna)
print(*numbers)
print('')
tamanho = len(numbers)
for k in range(tamanho):
if (not self.gameRunning):
self.tabuleiro = self.solvedBoard
break
self.tabuleiro[linha][coluna] = numbers[k]
self.solve()
if (self.gameRunning):
self.tabuleiro[linha][coluna] = 0
Note that the "show" function is only for debugging. I have an actual drawGrid function for handling the GUI.
My idea is to detect the first cell that needs to be changed, and, when the backtracking eventually comes back to It, with no other options to continue, It resets the board and then turns on a flag called "Impossible". Next iteration, the board detects that "Impossible" is on and resets the board, besides breaking the recursion.
However, that's seems too complicated, and I'm not sure how to do It without damaging code readability
What do you think? And also, Thanks in advance! This community is helping me a lot of this project :)

Counting weighted average doesn't work properly sometimes

I've made a program that counts weighted average and required weighted value to average being equal to our preference. If I want the average be equal to 85 from (the first value in the list is the weight of next values) [[4,72,78],[3,56],[6,93]] and x value of 6 weight it does not output the right value.
def choice(x):
c = 0
Choice = True
choices = []
while Choice:
if choices == []:
if x != 0:
fill = "weight of required value"
else:
fill = "weight of next values"
else:
if x != 0:
fill = "value of wanted weighted average"
else:
fill = "value"
try:
c = input("Give {}\n" .format(fill))
except:
continue
if isinstance(c, str):
if c == "":
Choice = False
if choices == []:
choices = False
break
else:
try:
choices.append(float(c))
except:
continue
if x != 0 and len(choices) == x:
break
c = 0
return choices
def av(x):
c = 0
alist = x[:]
alist.pop(0)
for a in alist:
c += a*x[0]
return c
def average(k,args):
c = 0
n = 0
for y in range(len(args)):
for a in range(len(args)):
c += (av(args[a]))/2
for b in range(len(args)):
n += (args[b][0]*(len(args[b])-1))/2
if k == 1:
return ([float("{0:.2f}".format(c/n)),c,n])
else:
j = float("{0:.2f}".format(c/n))
print("Weighted average {} from {}" .format(j,args))
def rmark(q,args):
alist = average(1,args)
a = float("{:.2f}" .format((((q[1]*(alist[2]+q[0]))-alist[1])/q[0])))
print("To get weighted average {}, u have to add the value equal to {} of weight {}" .format(q[1],a,q[0]))
# return a
Continue = True
list_choices = []
while Continue:
x = 0
x = choice(0)
if isinstance(x, list):
list_choices.append(x)
elif x == False:
break
print(list_choices)
rmark(choice(2),list_choices)
average(0,list_choices)
Let me break it down for you.
av function is reducing the size of your lists (x1, x2 and x3) to 1 by popping (alist.pop(0)) one element.
Hence, value of len(x1)-1 is 0, which means value of all multipliers in the denominator of (av(x1) + av(x2) + av(x3))/((x1[0]*(len(x1)-1)) + (x2[0]*(len(x2)-1)) + (x3[0]*(len(x3)-1))) is 0. Thus, the error divide by zero.

Identifying straight, flush and other categories (from Poker) using Python

I am newbie to python and learning it from books, forums and developers. Recently, I tried to implement various hand-ranking categories in the poker program. The goal is to calculate probabilities and see if it agrees with theoretical poker hand probabilities?
Source: https://en.wikipedia.org/wiki/Poker_probability?oldformat=true
Please find below the code and logic I've used so far to build it. The code contains Card and Deck classes which together implement a deck of standard playing cards used, as well as a sample PyTest test function test_xxx().
So far, I've written hasOnePair, hasTwoPairs, hasThreeOfAKind, hasFullHouse and hasFourOfaKind() functions and it is working fine but I am struggling with Straight, flush, StraightFlush.
Could someone please suggest or give guideline about how to approach straight, flush, royalflush, straightflush cases? Also, any further suggestions to update this code will be great.
import random
SUITS = ["Clubs", "Diamonds", "Hearts", "Spades"]
RANKS = ["", "Ace", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Queen", "King"]
# here's two Python classes whose objects represent playing cards and decks thereof
class Card():
"""
Represents a single playing card,
whose rank internally is int _rank: 1..13 => "Ace".."King"
and whose suit internally is int _suit 0..3 => "Clubs".."Spades"
"""
def __init__(self, rank=1, suit=3): # this is the constructor!
'''
Initialize card with given int suit and int rank
:param rank:
:param suit:
:return:
'''
self._rank = rank
self._suit = suit
def __str__(self): # this is the "stringifier"
"""
Return the string name of this card:
"Ace of Spades": translates int fields to strings
:return:
"""
# "Ace of Spades" is string for self._rank==1, self._suit==3
toreturn = RANKS[self._rank] + " of " + SUITS[self._suit]
return toreturn
class Deck():
"""
Represents a deck of 52 standard playing cards,
as a list of Card refs
"""
def __init__(self): # constructor
"""
Initialize deck: field _cards is list containing
52 Card refs, initially
:return: nothing
"""
self._cards = []
for rank in range(1, 14):
for suit in range(4):
c = Card(rank, suit) # create next Card with given value
self._cards.append(c) # add it to this Deck
def __str__(self):
"""
"Stringified" deck: string of Card named,
with \n for easier reading
:return:
"""
toreturn = ''
# for index in range(len(self._cards)):
# self._cards[index]
for c in self._cards:
temp = str(c) # temp is the stringified card
toreturn = toreturn + temp + "\n" # note \n at end
return toreturn
def shuffle(self):
random.shuffle(self._cards) # note random function to do this
def dealCard(self):
toreturn = self._cards.pop(0) # get and remove top card from deck
return toreturn
def buildDict(hand):
dict = {}
for card in hand:
dict[card._rank] = dict.get(card._rank, 0) + 1
return dict
def hasOnePair(dict):
twocount = 0
threecount = 0
for v in dict.values():
if v == 2:
twocount += 1
elif v == 3:
threecount += 1
if twocount==1 and threecount != 1:
return True
else:
return False
def hasTwoPairs(dict):
twocount1 = 0
threecount1 = 0
for v in dict.values():
if v ==2:
twocount1 += 1
elif v == 3:
threecount1 +=1
if twocount1 == 2 and threecount1 != 1:
return True
else:
return False
def hasThreeOfAKind(dict):
twocount = 0
threecount = 0
for v in dict.values():
if v == 2:
twocount += 1
elif v == 3:
threecount += 1
if twocount != 1 and threecount == 1:
return True
else:
return False
def hasFullHouse(dict):
twocount = 0
threecount = 0
for v in dict.values():
if v == 2:
twocount += 1
elif v == 3:
threecount += 1
if twocount == 1 and threecount == 1:
return True
else:
return False
def hasFourOfAKind(dict):
fourcount = 0
onecount = 0
for v in dict.values():
if v ==4:
fourcount += 1
elif v == 1:
onecount +=1
if fourcount == 1 and onecount == 1:
return True
else:
return False
def hasStraight(hand):
return False
def hasFlush(dict):
return False
def hasStraightFlush(dict):
return False
def hasRoyalFlush(dict):
return False
def main():
TRIALS = 1000 # int(input ("Input number of hands to test: "))
hand = [] # list of Card in hand
# accumulators for different counts
onepairCount = 0
twopairCount = 0
threeCount = 0
fourCount = 0
fullHouseCount = 0
StraightCount = 0
for num in range(TRIALS):
# create new Deck and shuffle
d = Deck()
d.shuffle()
# initialize hand as empty list
hand = []
# deal top 5 cards of deck, adding to hand
for count in range(5):
hand.append(d.dealCard())
# build the dictionary of card ranks in hand
dict = buildDict(hand)
# use dictionary to make hand checking easier
if hasOnePair(dict):
onepairCount += 1
elif hasTwoPairs(dict):
twopairCount += 1
elif hasThreeOfAKind(dict):
threeCount += 1
elif hasFourOfAKind(dict):
fourCount += 1
elif hasFullHouse(dict):
fullHouseCount += 1
elif hasStraight(dict):
StraightCount +=1
# add more if needed...
# print out results...
print("Number of one pair hands is: ", onepairCount)
print("% of hands: ", 100.0 * onepairCount / TRIALS)
print("Number of two pair hands is: ", twopairCount)
print("% of hands: ", 100.0 * twopairCount / TRIALS)
print("Number of trips hand is: ", threeCount)
print("% of hands: ", 100.0 * threeCount / TRIALS)
print("Number of quads hand is: ", fourCount)
print("% of hands: ", 100.0 * fourCount / TRIALS)
print("Number of trips hand is: ", fullHouseCount)
print("% of hands: ", 100.0 * fullHouseCount / TRIALS)
print("Number of trips hand is: ", StraightCount)
print("% of hands: ", 100.0 * StraightCount / TRIALS)
def card_example():
card1 = Card() # Card(1,3) => Ace of Clubs
card2 = Card(12, 2) # Card (12,2) => Queen of Hearts
card1._newfield = 47 # we can add new fields to any Python object!
# three ways of printing a Card
#
print(card1.__str__()) # calling the methods against card
print(str(card2)) # type-casting
print(card2) # short-cut: passing obj ref to print does str() automagically
print(card1._newfield) # see the new field value?
print(card1._rank) # see the rank (1..13)
print(card1._suit) # see the suit (0..3)
def deck_example():
"""
Test Deck: create, print then shuffle, print again
Then deal first two cards and print, along with bottom card
"""
deck = Deck()
print(str(deck)) # see entire deck before shuffling
print("Now we shuffle:\n")
deck.shuffle()
print(str(deck)) # see entire deck after shuffling
card1 = deck.dealCard()
card2 = deck.dealCard()
print("The first card dealt is", str(card1), "and the second is", str(card2))
print("Bottom of deck is", deck._cards[-1]) # can't hide the implementation!
if __name__ == "__main__": # only run this if this .py is NOT imported
# pass
# card_example() # uncomment to test creating & calling Card methods
# deck_example() # uncomment to test Deck: create, print, shuffle, print
main() # uncomment to run general poker odds calculations
#
# -------------------------------------------------------------------------
#
#pytest follows...
def test_one_pair():
testhand = [Card(2, 3), Card(1, 2),
Card(3, 1), Card(13, 2),
Card(2, 0)]
dict = buildDict(testhand)
assert hasOnePair(dict) #hasTwoPairs
def test_two_pair():
testhand = [Card(2, 3), Card(1, 2),
Card(3, 1), Card(3, 2),
Card(2, 0)]
dict = buildDict(testhand)
assert hasTwoPairs(dict)
def test_three_pair():
testhand = [Card(1, 3), Card(1, 2),
Card(1, 1), Card(13, 2),
Card(2, 0)]
dict = buildDict(testhand)
assert hasThreeOfAKind(dict)
def has_Four_Of_A_Kind():
testhand = [Card(1, 3), Card(1, 2),
Card(1, 1), Card(1, 0),
Card(2, 0)]
dict = buildDict(testhand)
assert hasFourOfAKind(dict)
def test_full_house():
testhand = [Card(1, 3), Card(1, 2),
Card(1, 1), Card(13, 2),
Card(13, 2)]
dict = buildDict(testhand)
assert hasFullHouse(dict)
def test_Straight():
testhand = [Card(11, 1), Card(10, 3),
Card(9, 2), Card(8, 1),
Card(7, 3)]
dict = buildDict(testhand)
assert hasStraight(dict)
The condition for a straight is for your five cards to have adjacent ranks, such that no two cards have the same rank. You can use two different checks to confirm this:
when sorted, the difference between the top card and the bottom card is equal to the total number of cards minus one(e.g. a 4-8 straight has a difference of 4)
no two cards in the straight have the same rank (thus by pigeonhole principle, all ranks between the minimum and maximum must be present
Here's a sample implementation of that:
def hasStraight(hand):
# account for both low-ace and high-ace
ranks_low = sorted([card._rank for card in hand])
ranks_high = sorted([(14 if card._rank == 1 else card._rank) for card in hand])
return (
(
ranks_low[-1] - (len(hand) - 1) == ranks_low[0]
or ranks_high[-1] - (len(hand) - 1) == ranks_high[0]
) # condition 1
and len(set(hand)) == len(hand) # condition 2
)
You will also have to account for low-aces and high-aces. You can do this by, for example, doing two similar checks, once with the normal card ranks and once by doing something like `if card._rank == 1: card._rank == 14
The condition for a flush is simply that all cards are the same suit. This is easy to verify, by simply making a set of all unique suits in the hand, and returning true if that set has only one element (thus all the suits must be the same):
def hasFlush(hand):
suits_set = set(*[card._suit for card in hand])
return len(suits_set) == 1
Once you have those, straight flush and royal flush are easy:
def hasStraightFlush(hand):
return hasStraight(hand) and hasFlush(hand)
def hasRoyalFlush(hand):
ranks = sorted([14 if card._rank == 1 else card._rank for card in hand])
return (
hasStraightFlush(hand)
and ranks[0] == 10 and ranks[-1] == 14
) # royal flush starts at 10, ends at high-ace

Resources