I made an algorithm to check whether which player won the game or is it a tie or the game is still not finished yet but it doesn't work well with some testcases.
I don't know what's wrong with my code. but it doesn't work so I think somethings off with my algorithm or the code itself.
so my algorithm basically is:
separate the moves of player1 and player 2
check if the moves of player 1 is in the winning moves if it is return 'player1'
check if the moves of player 2 is in the winning moves if it is return 'player2'
if there is no winner check if the number of moves is equal to 9. If it is return 'tie'
else if the number of moves is less than 9 return 'uncertain'
here is the code:
def match(a1,a2):
a1.sort()
for solution in a2:
if a1 == solution:
return True
return False
def ticTacToeWinner(moves, n):
# --- Variables ---
p1 = []
p2 = []
winning_moves = [
[[0,0],[0,1],[0,2]],
[[1,0],[1,1],[1,2]],
[[2,0],[2,1],[2,2]],
[[0,0],[1,0],[2,0]],
[[0,1],[1,1],[2,1]],
[[1,0],[2,1],[3,1]],
[[0,0],[1,1],[2,2]],
[[0,2],[1,1],[2,0]],
]
winner = None
# --- Loops ---
for i in range(1,n+1): # separate the moves for each player
if i % 2 == 0:
p2.append(moves[i-1])
else:
p1.append(moves[i-1])
# --- Conditionals ---
if len(p1) >= 3: # check if the player1 has more than 3 moves
if match(p1,winning_moves) == True: # check if the moves of player1 is in the winning moves
winner = 'player1' # winner is player 1
return winner
if len(p2) >= 3: # check if the player2 has more than 3 moves
if match(p2,winning_moves) == True: # check if the moves of player 2 is in the winning moves
winner = 'player2' # winner is player 2
return winner
if not winner: # there are no winners
if n == 9: # board is full so it's draw
return 'draw'
elif n < 9: # board is not full
return 'uncertain'
This code is my solution for this question:
https://www.codingninjas.com/codestudio/problems/tic-tac-toe-winner_1214545?topList=top-apple-coding-interview-questions
The code only works for 2 testcases there and incorrect for everything else
Example input and output:
ticTacToeWinner([[0,0],[2,2],[0,1],[1,1],[0,2]],5) -> 'player1'
since the moves of player1 is: [[0,0],[0,1],[0,2]] which makes a triple X at the top of the board
The match function at the top is the one that checks whether the move of a player is in the winning moves table.
here is one of the testcases that don't work with the code:
5
9
2 0
0 1
1 1
1 0
1 2
0 0
2 1
2 2
0 2
4
1 2
1 0
2 0
1 1
1
2 0
9
2 0
1 2
0 1
0 0
0 2
2 1
2 2
1 1
1 0
3
0 2
2 0
0 0
These are some issues:
winning_moves has a completely wrong row, which also has a 3 -- which is an invalid index.
Change:
[[1,0],[2,1],[3,1]],
to:
[[0,2],[1,2],[2,2]],
When len(p1) is greater than 3, you'll never find a win, because winning_moves does not have entries that have this size. Yet it is obvious that there could be a win
Even when len(p1) is equal to 3, you may still miss a win, because the order of the moves may not be like they are in winning_moves.
Not a problem, but elif n < 9 can just be else, as there are no other possible values for n.
You should really take a different approach: play the moves on a 3x3 board (a 2D list), and then use winning_moves to see what is at those winning positions. If it turns out they are all 3 a move of player1, you know that player1 won the game.
So here is what your function should look like:
def ticTacToeWinner(moves, n):
winning_moves = [
[[0,0],[0,1],[0,2]],
[[1,0],[1,1],[1,2]],
[[2,0],[2,1],[2,2]],
[[0,0],[1,0],[2,0]],
[[0,1],[1,1],[2,1]],
[[0,2],[1,2],[2,2]], # was wrong
[[0,0],[1,1],[2,2]],
[[0,2],[1,1],[2,0]],
]
# create a board:
board = [
[".", ".", "."],
[".", ".", "."],
[".", ".", "."],
]
# play the moves on the board:
for i, (row, col) in enumerate(moves):
board[row][col] = "12"[i % 2] # store the player's number in this cell
# check who has played on the winning patterns
for winning_line in winning_moves:
played = [board[row][col] for row, col in winning_line]
if played[0] != "." and played[0] == played[1] == played[2]:
return "player" + played[0]
if n == 9: # board is full so it's draw
return 'draw'
else: # board is not full
return 'uncertain'
I'm pretty sure that one of your problems is that you don't handle the case of more than 3 moves...
I'd advise to change the type of a single "move" to a tuple, which is hashable. then, you'd be able to use this elegant match code:
def match(player_moves_list: List[tuple], winning_moves:List[List[tuple]]):
for solution in winning_moves:
if set(solution).issubset(player_moves):
return True
return False
to change the list elements to tuple elements, simply turn brackets to parenthesis:
winning_moves = [
[(0,0),(0,1),(0,2)],
...,
[(0,2),(1,1),(2,0)],
]
Related
I have programmed a program of tic tac toe in python where I use the following code to determine if a row / column / diagonal consists of only ones or twos to determine if any of player one or two have won (I use ones and twos instead of x and o). The problem is that I want the winner to be chosen when there are three in a row regardless of the size of the board and with this code this only works for a board of size 3x3.
def check_row(board, player):
'''
Checks row for a win
'''
return any(all(row == player)
for row in board)
def check_column(board, player):
'''
Checks column for a win
'''
# We use transpose() to be able to check the column by using the definition
# for check_row
return check_row(board.transpose(), player)
def check_diagonal(board, player):
'''
Checks diagonal for a win
'''
# We use np.diag() and np.fliplr() to be able to check the diagonals
# of the game board
return any(all(np.diag(board_rotation) == player)
for board_rotation in [board, np.fliplr(board)])
For example, if I choose a board with size 5x5, the winner will be, with this code, when it is five in a row and not three in a row. I use np.zeros ((board_size, board_size), dtype = int) to create a game board and I have considered whether you can use something like column_1 == column_2 == column_3 to determine if there are three in a row:
if board[1][1] != 0:
if board[1][1] == board[0][0] == board[2][2]:
return board[1][1]
elif board[1][1] == board[0][2] == board[2][0]:
return board[1][1]
But also for this to work in a 5x5 game board I have to write down all possible ways to get three in a row in a 5x5 board which will give an extremely long code. Any tips to improve this?
This is my fully code now:
''' 403-0047-MPO '''
import numpy as np
import random
import matplotlib.pyplot as plt
# scenario = What scenario you would like to play, scenario one or scenario two
# game_size = The size of game board, 3x3, 5x5 or 7x7
# games = The number of game rounds you would like to play
# board = The game board
def create_board(board_size):
'''
Creating an empty board
'''
return np.zeros((board_size,board_size), dtype=int)
def available_spaces(board):
'''
Looking for available spaces on the game board
This is used in both scenario one and two
'''
lst = []
for i in range(len(board)):
for j in range(len(board)):
if board[i][j] == 0:
lst.append((i, j))
return(lst)
def make_random_move(board, player):
'''
Randomly places the player's marker on the game board
This is used in both scenario one and two
'''
spaces = available_spaces(board)
space = random.choice(spaces)
board[space] = player
return board
def available_space_middle(board, board_size):
'''
Checks that the center position is available for all game boards
This is only used in scenario two
'''
# Using if-statements to find the middle for all board_size ∈ {3, 5, 7}
# and check that they are empty, this also avoids placing the marker there repeatedly.
lst = []
for i in range(len(board)):
for j in range(len(board)):
if board_size == 3:
if board[i][j] == 0:
lst.append((1, 1))
if board_size == 5:
if board[i][j] == 0:
lst.append((2, 2))
if board_size == 7:
if board[i][j] == 0:
lst.append((3, 3))
return(lst)
def make_move_middle(board, player, board_size):
'''
Places the first marker for player one in the center of the game board.
This is only used in scenario two
'''
# Using player = 1 we define that the first move in scenario two should be
# made by player one using the marker 1
player = 1
selection = available_space_middle(board, board_size)
current_loc = random.choice(selection)
board[current_loc] = player
return board
def check_row(board, player):
'''
Checks row for a win
'''
return any(all(row == player)
for row in board)
def check_column(board, player):
'''
Checks column for a win
'''
# We use transpose() to be able to check the column by using the definition
# for check_row
return check_row(board.transpose(), player)
def check_diagonal(board, player):
'''
Checks diagonal for a win
'''
# We use np.diag() and np.fliplr() to be able to check the diagonals
# of the game board
return any(all(np.diag(board_rotation) == player)
for board_rotation in [board, np.fliplr(board)])
def evaluate(board):
'''
Evaluates the game board for a winner
'''
# Here we bring out the winner and define 1 for when player one wins,
# 2 for when player two wins and -1 for ties.
winner = 0
for player in [1, 2]:
if (check_row(board, player) or
check_column(board,player) or
check_diagonal(board,player)):
winner = player
if np.all(board != 0) and winner == 0:
winner = -1
return winner
def play_game(scenario, board_size):
'''
Defines the game for scenario one and scenario two
'''
# By using if-statements and a while-loop we define how the game should
# go in scenario one and scenario two respectively.
# By using break, we also ensure that the loop (game) ends if a winner has been found.
board, winner, counter = create_board(board_size), 0, 1
if scenario == 1:
while winner == 0:
for player in [1, 2]:
board = make_random_move(board, player)
# Remove the hashtags below to check the game board step by step
print("Board after " + str(counter) + " move")
print(board)
counter += 1
winner = evaluate(board)
if winner != 0:
break
return winner
if scenario == 2:
while winner == 0:
for player in [2, 1]:
board = make_move_middle(board, player, board_size)
board = make_random_move(board, player)
# Remove the hashtags below to check the game board step by step
print("Board after " + str(counter) + " move")
print(board)
counter += 1
winner = evaluate(board)
if winner != 0:
break
return winner
def save_stats(games, scenario, board_size):
'''
Saves data from all game rounds
'''
# By using the previously defined 1,2 and -1 we add one point for each win
# for player1wins, player2wins and ties.
player1wins=0
player2wins=0
ties=0
for game in range(games):
result=play_game(scenario, board_size)
if result==-1: ties+=1
elif result==1: player1wins+=1
else: player2wins+=1
return [player1wins, player2wins, ties] # for returning
def print_stats(games, scenario, board_size):
'''
Presents data from all game rounds
'''
player1wins, player2wins, ties = save_stats(games, scenario, board_size)
print('Player 1 wins:',player1wins)
print('Player 2 wins:',player2wins)
print('Tie:',ties)
# Data
height = [player1wins, player2wins, ties]
bars = ('Player 1', 'Player 2', 'Tie')
y_pos = np.arange(len(bars))
# Create bars and choose color
plt.bar(y_pos, height, color = (0.5,0.1,0.5,0.6))
# Limits for the Y axis
plt.ylim(0,games)
# Create names
plt.xticks(y_pos, bars)
# Saves a pdf with data
plt.savefig('utfall.pdf') # Saves the data as 'utfall.pdf'
plt.close()
def main():
'''
This is the main body of the program
'''
# Using try: and except: to raise ValueError and
# while-loops to handle incorrect input. The question will be asked again
# if the input value is wrong and continue via break if it is correct.
try:
while True:
games = int(input("How many games do you want to simulate? "))
if games <1:
print('The number of game rounds should be grater than zero, try again')
else:
break
while True:
board_size = int(input("How big playing surface (3/5/7)? "))
if not (board_size == 3 or board_size == 5 or board_size == 7):
print('Board size does not exist, try again')
else:
break
while True:
scenario = int(input('What scenario (1/2)? '))
if not (scenario == 1 or scenario == 2):
print('Scenario does not exist, try again')
else:
break
print_stats(games, scenario, board_size)
except ValueError:
print('All questions must be answered correctly for the game to start, try again')
main()
I will post a idea using numpy.split
You split your board in chunks of 3, but starting in different positions: 0, 1 and 2.
Explanation: Consider [a,b,c,d,f,g]
Starting from 0 -> [a,b,c], [d,f,g]
Starting from 1 -> [b,c,d]
Starting from 2 -> [c,d,f]
so, all possibilities are there
You check if the elements are equal 1 or 2 and sum over the boolean mask. If the sum is 3, then there is a chunk with 3 equal elements.
Example code to search from winners on the rows:
import numpy as np
#random array just for test
board = np.array([[1,-1,-1,-1,1,0],
[1,1,0,1,1,0],
[1,1,0,1,1,0],
[1,1,0,1,1,0],
[1,1,0,1,1,0]])
winner = None
#iterate on i to start from position 0, 1 and 2
for i in range(3):
#We want the lengh of the array a multiple of 3, so we must correct were it ends:
rest = -(board[:,i:].shape[1]%3)
if rest == 0:
#0 would return a empty list, so if it is the case, we will change to None
rest = None
#Now, we calculate how manny divisions we need to get chunks of 3 (on the rows)
n = (board[:,i:].shape[1])//3
#Now divide the board in chunks
chunks = np.split(board[:,i:rest],n,axis = 1)
for matrix in chunks:
#sum over all rows of 3 elements
sumation = np.sum(matrix == 1,axis = 1)
if 3 in sumation:
winner = 1
sumation = np.sum(matrix == 2,axis = 1)
if 3 in sumation:
winner = 2
Well, maybe a little confusing, but i believe it will work, or at least give you some idea
I've created a function winProbability(ra, rb, n) and I want to simulate n games in order to estimate the probability that a player with the ability ra will win a game against a player with ability rb
I'll show the code I've done so far. If this seems like a easy issue it's because I am new to coding.
import random #import random allows for the use of randomly generated numbers
def game(ra, rb): #this function game sets out the way the game runs
p_a_point = ra/(ra+rb) #this line of code determines the probability that
#player a wins any given point
a_points = 0 #the amount of points player a has is defaulted to 0
b_points = 0 #the amount of points player b has is defaulted to 0
score_to_win = 11 #the winning score is defaulted to 11
while (a_points < score_to_win and b_points < score_to_win) or abs (a_points - b_points) < 2: #while player a's points and player b's points are less than the winning score:
p_b_point = random.random()#the probability b wins a point is set the a random value between 0 and 1
if p_b_point < p_a_point: #is the probability b wins a point is less than the probability a wins a point:
a_points = a_points + 1 #a wins 1 point
else: #if player a doesn't win a point:
b_points = b_points + 1 #b wins a point
return(a_points, b_points)#both players points are returned
print(game(70,30))#the function is called with two integer values as parameters
def winProbability(ra, rb, n):
To be honest from here I am unsure on how to go about this. I was thinking of doing a for loop so for example:
for n in game (ra, rb):
but I am unsure if I can use a previous function in this loop call. I'm also confused on how to calculate probabilities in code
The general aim is to call the function with two probabilities for example 70 and 30 and give a decimal answer for the probability player ra will win.
To previous commenters, I apologise for being vague before. I've never posted on here before.
See if this helps.
from random import randint, seed
seed()
rounds = input(" How many rounds will be played in the match? ")
print("\n Please enter skill levels as integers from 0 to 99.\n")
a = input(" What is the skill level of player 1? ")
b = input(" What is the skill level of player 2? ")
# Catch empty inputs
if not rounds: rounds = 10
if not a: a = 0
if not b: b = 0
# Python inputs are always strings. Convert them to integers.
rounds = int(rounds)
a = int(a)
b = int(b)
# If both skill levels are 0, set them to 1.
# (This will avoid a possible division by zero error.)
if a+b == 0: a = b = 1
# Catch and correct numbers that are too high.
if a > 99: a = 99
if b > 99: b = 99
# Convert integer skill levels to values between 0.0 and 0.99
a = a/100
b = b/100
print()
print(" Chance player 1 will win: "+str(int(100*a/(a+b)))+" percent.")
print(" Chance Player 2 will Win: "+str(int(100*b/(a+b)))+" percent.")
print()
for x in range(rounds):
roll = randint(0,999)/1000
print("roll =",roll, end =": ")
if roll <= a/(a+b): # <-- Compare roll to ratio of player skill levels.
print("Round "+str(x+1)+" Winner: Player 1")
else:
print("Round "+str(x+1)+" Winner: Player 2")
print()
this was my answer
import random
def winProbability(ra, rb, n):
winCount = 0 #used to count how many times 'a' won
probabilityRange = ra + rb
for i in range(n):
# pick a number between 0 and probabiilityRange
number = random.randint(0, probabilityRange)
# if number is < ra then 'a' won if number is > ra then 'b' won if number == ra then results in a draw
if number < ra:
winCount += 1
print ('win')
if number > ra:
print('loss')
if number == ra:
print ('draw') # draw doesn't count as win
return winCount*(100/n)
print (winProbability(10000,1,100000))
This prints the results of each game played, and returns the possibility that 'a' will win in percentile form.
This is somewhat similar to this question : Python generate all possible configurations of numbers on a "board" but I'm trying to implement in Python and I want to include generated boards that are just partially complete.
In tic tac toe there are 3^9 (19683) board positions.
Here is my code to generate each board where each element of the board array is a single board :
boards = []
temp_boards = []
for i in range(0 , 19683) :
c = i
temp_boards = []
for ii in range(0 , 9) :
temp_boards.append(c % 3)
c = c // 3
boards.append(temp_boards)
0 corresponds to O
1 corresponds to X
2 corresponds to 'position not yet filled'
Could I have missed any positions ?
If you consider that players take turns and the game stops when someone wins, there will be fewer possible boards than the maximum combinations of X, O and blank cells would suggest. You also cannot count boards filled with all Xs or all Os or any combinations that would have a difference greater than 1 between the number of Xs and Os.
You can obtain that number by recursively simulating moves starting with X and starting with O:
axes = [(0,1,2),(3,4,5),(6,7,8),(0,3,6),(1,4,7),(2,5,8),(0,4,8),(2,4,6)]
def isWin(board):
return any("".join(board[p] for p in axis) in ["XXX","OOO"] for axis in axes)
def validBoards(board="."*9,player=None):
if player == None:
yield board # count the empty board
for b in validBoards(board,player="X"): yield b # X goes 1st
for b in validBoards(board,player="O"): yield b # O goes 1st
return
opponent = "XO"[player=="X"]
for pos,cell in enumerate(board):
if cell != ".": continue
played = board[:pos]+player+board[pos+1:] # simulate move
yield played # return the new state
if isWin(played): continue # stop game upon winning
for nextBoard in validBoards(played,opponent):
yield nextBoard # return boards for subsequent moves
output:
distinctBoards = set(validBoards()) # only look at distinct board states
allStates = len(distinctBoards)
print(allStates) # 8533 counting all intermediate states
winningStates = sum(isWin(b) for b in distinctBoards)
print(winningStates) # 1884 (so 942 for a specific starting player)
filledStates = sum(("." not in b) for b in distinctBoards)
print(filledStates) # 156 states where all cells are filled
finalStates = sum(isWin(b) or ("." not in b) for b in distinctBoards)
print(finalStates) # 1916 end of game states (win or draw)
earlyWins = sum(isWin(b) and ("." in b) for b in distinctBoards)
print(earlyWins) # 1760 wins before filling the board
draws = finalStates - winningStates
print(draws) # 32 ways to end up in a draw
lastWins = filledStates-draws
print(lastWins) # 124 wins on the 9th move (i.e filling the board)
fastWins = sum( isWin(b) and b.count(".") == 4 for b in distinctBoards)
print(fastWins) # 240 fastest wins by 1st player (in 3 moves)
fastCounters = sum( isWin(b) and b.count(".") == 3 for b in distinctBoards)
print(fastCounters) # 296 fastest wins by 2nd player (in 3 moves)
If you need a faster implementation, here is an optimized version of the function that only returns distinct states and leverages this to skip whole branches of the move sequence tree:
def validBoards(board="."*9,player=None,states=None):
if player == None:
result = {board} # count the empty board
result |= validBoards(board,player="X",states=set()) # X goes 1st
result |= validBoards(board,player="O",states=set()) # O goes 1st
return result
opponent = "XO"[player=="X"]
for pos,cell in enumerate(board):
if cell != ".": continue
played = board[:pos]+player+board[pos+1:] # simulate move
if played in states : continue # skip duplicate states
states.add(played) # return the new state
if isWin(played): continue # stop game upon winning
validBoards(played,opponent,states) # add subsequent moves
return states
My loop is looping infinitely I am pretty sure it is not supposed to happen?
Here is my code:
def move(x, movie, g):
global boo
while boo:
ij = 0
for i in g:
ij += 1
if ij == movie and g[ij - 1] == "":
g[ij - 1] = x
boo = False
break
elif ij == 9 and movie == 9 and not g[ij - 1] == "":
movie = input("Already occupied try again: ")
boo = False
return g
I am trying to make a function for a tic tac toe game.
Update here is the required details:
import md
g = ["", "", "o", "o", "", "", "", "", ""]
md.move("x", 4, g)
print(g)
Gets stuck in the occupied part of the function
Neither of your stopping conditions are met:
ij == movie and g[ij - 1] == ""
when ij == movie (i.e. ij is 4), then g[ij-1] (i.e. g[3]) is not blank, it is "o"
ij == 9 and movie == 9 and not g[ij - 1] == ""
when ij is 9, movie is not 9
So boo never gets set to False and the While loop repeats forever
By debugging manually boo can’t be set to False.
Take a look carefully at your code:
When ij equals 4, g[3] equals o. Your first condition fails.
The second condition can’t succeed at any time because movie equals 4. Whereas 9 is expected.
Regards.
I am implementing a Python version of the game Othello / Reversi. However, my algorithm seems to be having trouble when searching in the southwest direction.
Here are some important functions to understand how my current code works:
def _new_game_board(self)->[[str]]:
board = []
for row in range(self.rows):
board.append([])
for col in range(self.columns):
board[-1].append(0)
return board
def _is_valid_position(self, turn:list)->bool:
'''return true if the turn is a valid row and column'''
row = int(turn[0]) - 1
column = int(turn[1]) - 1
if row >= 0:
if row < self.rows:
if column >= 0:
if column < self.columns:
return True
else:
return False
def _is_on_board(self, row:int, col:int)->bool:
'''returns true is coordinate is on the board'''
if row >=0:
if row < self.rows:
if col >=0:
if col < self.columns:
return True
def _searchNorthEast(self)->None:
'''Search the board NorthEast'''
print("NorthEast")
row = self.move_row
column = self.move_column
should_be_flipped = list()
row += 1
column -= 1
if self._is_on_board(row, column):
print("column searching NorthEast on board")
if self.board[row][column] == self._opponent:
should_be_flipped.append([row, column])
while True:
row += 1
column -= 1
if self._is_on_board(row, column):
if self.board[row][column] == self._opponent:
should_be_flipped.append([row, column])
continue
elif self.board[row][column] == self.turn:
self._to_be_flipped.extend(should_be_flipped)
break
else:
break
else:
self._to_be_flipped.extend(should_be_flipped)
else:
pass
def _searchSouthWest(self)->None:
'''Search the board SouthWest'''
print("in SouthWest")
row = self.move_row
column = self.move_column
should_be_flipped = list()
row -= 1
column += 1
if self._is_on_board(row, column):
print("column searching SouthWest on board")
if self.board[row][column] == self._opponent:
should_be_flipped.append([row, column])
while True:
row -= 1
column += 1
if self._is_on_board(row, column):
if self.board[row][column] == self._opponent:
should_be_flipped.append([row, column])
continue
elif self.board[row][column] == self.turn:
self._to_be_flipped.extend(should_be_flipped)
break
else:
break
else:
self._to_be_flipped.extend(should_be_flipped)
else:
pass
def _move_is_valid(self, turn:list)->bool:
'''Verify move is valid'''
self._to_be_flipped = list()
self._opponent = self._get_opposite(self.turn)
if self._is_valid_position(turn):
self.move_row = int(turn[0]) - 1
self.move_column = int(turn[1]) - 1
self._searchRight()
self._searchLeft()
self._searchUp()
self._searchDown()
self._searchNorthWest()
self._searchNorthEast
self._searchSouthEast()
self._searchSouthWest()
if len(self._to_be_flipped) > 0:
return True
else:
return False
Now let's say the current board looks like the following:
. . . .
W W W .
. B B .
. B . .
Turn: B
and the player makes a move to row 1 column 4, it says invalid because it does not detect the white piece in row 2 column 3 to be flipped. All my other functions are written the same way. I can get it to work in every other direction except in this case.
Any ideas why it is not detecting the piece in this diagonal direction?
Don't Repeat Yourself. The _search* methods are extremely redundant which makes it difficult to see that the signs in
row -= 1
column += 1
are correct. Since you've given only two directions (NE, SW) and no documentation of the board orientation, I cannot tell if the signs agree with the board layout or even agree with themselves.
The _search* methods are also too long and should be divided into multiple functions, but that's a secondary concern.
I agree with msw about not repeating stuff. It is tempting to go ahead and do what you can once you see it, but generalizing will save you the headaches of debugging.
Here is some pseudocode that should give the general idea. I might not be able to finagle working with your code, but hopefully this shows how to reduce repetitive code. Note it doesn't matter if -1 is up or down. The board class is simply a 2x2 array of (open square/player 1's piece/player 2's piece,) along with whose turn it is to move.
# x_delta and y_delta are -1/0/1 each based on which of the up to 8 adjacent squares you are checking. Player_num is the player number.
def search_valid(x_candidate, y_candidate, x_delta, y_delta, board, player_num):
y_near = y_candidate + y_delta
x_near = x_candidate + x_delta
if x_near < 0 or x_near >= board_width:
return False
if y_near < 0 or y_near >= board_width:
return False # let's make sure we don't go off the board and throw an exception
if board.pieces[x_candidate+x_delta][y_candidate+y_delta] == 0:
return False #empty square
if board.pieces[x_candidate+x_delta][y_candidate+y_delta] == player_num:
return False #same color piece
return True #if you wanted to detect how many flips, you could use a while loop
Now a succinct function can loop this search_valid to see whether a move is legal or not e.g.
def check_valid_move(x_candidate, y_candidate, board, player_num):
for dx in [-1, 0, 1]:
for dy in [-1, 0, 1]:
if not x and not y:
continue # this is not a move. Maybe we don't strictly need this, since board.pieces[x_candidate+x_delta][y_candidate+y_delta] == player_num anyway, but it seems like good form.
if search_valid(x_candidate, y_candidate, dx, dy, board, player_num):
return True
return False
A similar function could actually flip all the opposing pieces, but that's a bit trickier. You'd need a while function inside the for loops. But you would not have to rewrite code for each direction.