mirror of
https://github.com/neogeek23/Tic-Tac-Toe.git
synced 2026-02-04 10:58:17 +00:00
Add files via upload
Added new display feature for 3 space. 3 space now displays as horizontal repetition of 2 space. Fixed issue with some victory paths in 4 space an above not being recognized. Previously some intermediate/interior cross sectional paths were not recognized when filled with n+1 of the same token as being a successful victory condition.
This commit is contained in:
parent
8ab1ce875c
commit
35a23fc8f2
166
tictactoe.py
166
tictactoe.py
@ -1,6 +1,7 @@
|
|||||||
import sys
|
import sys
|
||||||
import math
|
import math
|
||||||
import random
|
import random
|
||||||
|
import itertools
|
||||||
|
|
||||||
|
|
||||||
class Player:
|
class Player:
|
||||||
@ -11,9 +12,9 @@ class Player:
|
|||||||
if index < 0:
|
if index < 0:
|
||||||
self.__token = "-"
|
self.__token = "-"
|
||||||
elif index == 0:
|
elif index == 0:
|
||||||
self.__token = "O"
|
|
||||||
else:
|
|
||||||
self.__token = "X"
|
self.__token = "X"
|
||||||
|
else:
|
||||||
|
self.__token = "O"
|
||||||
|
|
||||||
def get_id(self):
|
def get_id(self):
|
||||||
return self.__index
|
return self.__index
|
||||||
@ -79,7 +80,7 @@ class Board:
|
|||||||
if d < 3:
|
if d < 3:
|
||||||
header = "\n "
|
header = "\n "
|
||||||
for i in range(self.__dimensions + 1):
|
for i in range(self.__dimensions + 1):
|
||||||
if i > 9: # lets be honest 100^99 will destroy memory
|
if i > 9: # lets be honest 100^99 will destroy memory; already dies a dim 8, 7 is struggle
|
||||||
header += "| " + str(i)
|
header += "| " + str(i)
|
||||||
else:
|
else:
|
||||||
header += "| " + str(i) + " "
|
header += "| " + str(i) + " "
|
||||||
@ -91,6 +92,25 @@ class Board:
|
|||||||
for j in i:
|
for j in i:
|
||||||
row_string += "| " + j.get_owner() + " "
|
row_string += "| " + j.get_owner() + " "
|
||||||
print(row_string)
|
print(row_string)
|
||||||
|
elif d < 4:
|
||||||
|
header = "\n"
|
||||||
|
for i in range(self.__dimensions + 1):
|
||||||
|
if i != 0:
|
||||||
|
header += "\t"
|
||||||
|
header += " {:2s}".format(str(i)[:1])
|
||||||
|
for j in range(self.__dimensions + 1):
|
||||||
|
header += "| {:2s}".format(str(j)[:1])
|
||||||
|
print(header)
|
||||||
|
for i in range(self.__dimensions + 1):
|
||||||
|
row_string = ""
|
||||||
|
for j in range(self.__dimensions + 1):
|
||||||
|
if j != 0:
|
||||||
|
row_string += "\t"
|
||||||
|
row_string += " {:2s}".format(str(i)[:1])
|
||||||
|
for k in range(self.__dimensions + 1):
|
||||||
|
# One of these days I'll think about why it is j-i-k not i-j-k #madness
|
||||||
|
row_string += "| " + list_of_list[j][i][k].get_owner() + " "
|
||||||
|
print(row_string)
|
||||||
else:
|
else:
|
||||||
for i in range(self.__dimensions + 1):
|
for i in range(self.__dimensions + 1):
|
||||||
if i < 10:
|
if i < 10:
|
||||||
@ -149,82 +169,80 @@ class Board:
|
|||||||
return self.place_token(self.__get_random_coordinate(0, self.__dimensions + 1), token)
|
return self.place_token(self.__get_random_coordinate(0, self.__dimensions + 1), token)
|
||||||
|
|
||||||
def __is_winning_move(self, coordinate, player):
|
def __is_winning_move(self, coordinate, player):
|
||||||
intersecting_paths = list()
|
coord_list = list()
|
||||||
indexes = list()
|
for s in coordinate.split("."):
|
||||||
|
coord_list.append(int(s))
|
||||||
|
|
||||||
for i in coordinate.split("."):
|
freedom_set = self.__get_dimension_locks()
|
||||||
indexes.append(int(i))
|
intersecting_pathsets = self.__get_winning_paths(coord_list, freedom_set)
|
||||||
|
|
||||||
# Get all of the coordinate for straight (single dimension change) paths that intersect our coordinate
|
for paths in intersecting_pathsets:
|
||||||
for i in range(self.__dimensions):
|
for path in paths:
|
||||||
temp_list = list()
|
solution_in_path = True
|
||||||
for j in range(self.__dimensions + 1):
|
for space in path:
|
||||||
temp_coord = indexes.copy()
|
solution_in_path = solution_in_path and self.__get_space(space).get_owner() == player.get_token()
|
||||||
temp_coord[i] = j
|
if solution_in_path:
|
||||||
temp_list.append(temp_coord)
|
self.__winning_path = path
|
||||||
intersecting_paths.append(temp_list)
|
return player.get_id()
|
||||||
|
|
||||||
# Attempt to build all possible diagonal (multiple dimension change) paths that intersect our coordinate
|
|
||||||
# I feel like this might be over kill but with no limits on dimensions it gets confusing, better safe
|
|
||||||
intersecting_paths += self.__get_diagonals(indexes, -1, 1)
|
|
||||||
intersecting_paths += self.__get_diagonals(indexes, 1, -1)
|
|
||||||
intersecting_paths += self.__get_diagonals(indexes, -1, -1)
|
|
||||||
intersecting_paths += self.__get_diagonals(indexes, 1, 1)
|
|
||||||
|
|
||||||
for path in intersecting_paths:
|
|
||||||
solution_in_path = True
|
|
||||||
for space in path:
|
|
||||||
solution_in_path = solution_in_path and self.__get_space(space).get_owner() == player.get_token()
|
|
||||||
if solution_in_path:
|
|
||||||
self.__winning_path = path
|
|
||||||
return player.get_id()
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# This will produce a list of all possible diagonals of a given coordinate set
|
def __get_dimension_locks(self):
|
||||||
def __get_diagonals(self, indexes, up, dn):
|
final = list()
|
||||||
intersecting_paths = list()
|
initial = list()
|
||||||
for i in range(self.__dimensions - 1): # For each dimension less the first, no one cares about that
|
|
||||||
temp_list = list()
|
|
||||||
for j in range(self.__dimensions + 1): # For each coordinate in a path, since 1 extra we do + 1
|
|
||||||
if j == 0:
|
|
||||||
temp_coord = indexes.copy()
|
|
||||||
else:
|
|
||||||
temp_coord = temp_list[j - 1].copy()
|
|
||||||
if j > 0: # by skipping the first item, ensure our coordinate is included
|
|
||||||
for k in range(self.__dimensions - i): # For each point in a coordinate
|
|
||||||
if indexes[k + i] >= self.__dimensions / 2: # Below ensures any coordinate in range
|
|
||||||
temp_coord[k + i] += up + (self.__dimensions + 1 if temp_coord[k + i] + up < 0 else 0) \
|
|
||||||
- (self.__dimensions + 1 if temp_coord[k + i] + up > self.__dimensions else 0)
|
|
||||||
else:
|
|
||||||
temp_coord[k + i] += dn + (self.__dimensions + 1 if temp_coord[k + i] + dn < 0 else 0) \
|
|
||||||
- (self.__dimensions + 1 if temp_coord[k + i] + dn > self.__dimensions else 0)
|
|
||||||
if 0 < i:
|
|
||||||
dim_locked_coord = indexes[:i] + temp_coord[i:] # Should be allowing dimensional cross sections
|
|
||||||
else:
|
|
||||||
dim_locked_coord = temp_coord
|
|
||||||
temp_list.append(dim_locked_coord)
|
|
||||||
include_in_temp_list = self.__is_slope_correct(temp_list, i) # Skip indexes below i for cross section
|
|
||||||
if include_in_temp_list:
|
|
||||||
intersecting_paths.append(temp_list)
|
|
||||||
return intersecting_paths
|
|
||||||
|
|
||||||
# This will determine if the change in between all coordinate sets in a coordinate group is equal by ensuring that
|
for i in range(self.__dimensions):
|
||||||
# for each coordinate set in a coordinate group there is another coordinate set in which the difference between them
|
initial.append(True)
|
||||||
# is a net of 1 for each coordinate in of the coordinate sets
|
|
||||||
@staticmethod
|
for i in range(len(initial)):
|
||||||
def __is_slope_correct(coord_list, index_start):
|
if i > 0:
|
||||||
slope_is_correct = True
|
initial[i - 1] = False
|
||||||
for i in range(len(coord_list)):
|
for j in list(itertools.permutations(initial)):
|
||||||
found_a_slope_match = False
|
temp = list()
|
||||||
for j in range(len(coord_list)):
|
for k in list(j):
|
||||||
if i != j:
|
temp.append(k)
|
||||||
this_point_in_slope = True
|
if temp not in final:
|
||||||
for k in range(len(coord_list[i])):
|
final.append(temp)
|
||||||
if k >= index_start:
|
return final
|
||||||
this_point_in_slope = this_point_in_slope and abs(coord_list[i][k] - coord_list[j][k]) == 1
|
|
||||||
found_a_slope_match = found_a_slope_match or this_point_in_slope
|
def __get_winning_paths(self, coord_list, freedoms):
|
||||||
slope_is_correct = slope_is_correct and found_a_slope_match
|
path_set = list()
|
||||||
return slope_is_correct
|
# This will get all possible paths that are 5 long from the starting point
|
||||||
|
for freedom in freedoms:
|
||||||
|
result = list()
|
||||||
|
for edit_pattern in freedoms:
|
||||||
|
patterned_edit = list()
|
||||||
|
for i in range(self.__dimensions + 1):
|
||||||
|
temp = coord_list.copy()
|
||||||
|
for j in range(len(temp)):
|
||||||
|
if freedom[j]:
|
||||||
|
if edit_pattern[j]:
|
||||||
|
temp[j] = (temp[j] + i + (self.__dimensions + 1)) % (self.__dimensions + 1)
|
||||||
|
else:
|
||||||
|
temp[j] = (temp[j] - i - (self.__dimensions + 1)) % (self.__dimensions + 1)
|
||||||
|
patterned_edit.append(temp)
|
||||||
|
if patterned_edit not in result:
|
||||||
|
result.append(patterned_edit)
|
||||||
|
if result not in path_set:
|
||||||
|
path_set.append(result)
|
||||||
|
|
||||||
|
# This will prune out impossible paths (paths that wrap around the space)
|
||||||
|
for paths in path_set:
|
||||||
|
for path in paths:
|
||||||
|
slope_is_legit = True
|
||||||
|
for i in range(len(path)):
|
||||||
|
found_a_slope_buddy = False
|
||||||
|
for j in range(len(path)):
|
||||||
|
if i != j:
|
||||||
|
point_has_good_slope = True
|
||||||
|
for k in range(len(path[i])):
|
||||||
|
if freedoms[k]:
|
||||||
|
point_has_good_slope = point_has_good_slope and abs(path[i][k] - path[j][k]) == 1
|
||||||
|
found_a_slope_buddy = found_a_slope_buddy or point_has_good_slope
|
||||||
|
slope_is_legit = slope_is_legit and found_a_slope_buddy
|
||||||
|
if not slope_is_legit:
|
||||||
|
paths.remove(path)
|
||||||
|
|
||||||
|
return path_set
|
||||||
|
|
||||||
def get_winning_path(self):
|
def get_winning_path(self):
|
||||||
result = ""
|
result = ""
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user