Skip to content

Commit 193a90d

Browse files
committed
8.x updates
1 parent 49e962a commit 193a90d

5 files changed

+1450
-0
lines changed

gamev8.1_fevertime_logic.py

+347
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,347 @@
1+
# v8 logic =
2+
# v7 logic + set pooling + different algos for each mode - skimming(incompatible with v8 logic)
3+
# basically have a set of dead pools
4+
# once a board dies, it goes into the hashed set
5+
# taking of set union lookup O(1)
6+
7+
# v8.1
8+
# removed atrocious deepcopy from the code
9+
10+
11+
from copy import Error
12+
from typing import List
13+
import itertools
14+
from itertools import repeat
15+
import numpy as np
16+
import pprint
17+
import heapq
18+
import copy
19+
20+
import numpy
21+
import time
22+
23+
24+
# improved branching algorithm by creating local and global pools, releasing memory on local pools when they are found useless
25+
26+
27+
# practicing the logic in fevertime before applying it to the main game
28+
29+
30+
31+
32+
start = time.time()
33+
34+
35+
36+
MAX_INT=15
37+
38+
REMOVED_FROM_MEMORY=None
39+
40+
41+
class Path:
42+
# only used to show a non-zero lost path
43+
def __init__(self, sel:int, tar:int) -> None:
44+
self.sel=sel
45+
self.tar=tar
46+
def __repr__(self) -> str:
47+
return f"<Path {self.sel}->{self.tar}>"
48+
49+
50+
class BoardLoc:
51+
def __init__(self, lost:int, id:int) -> None:
52+
self.lost=lost
53+
self.id=id
54+
def __repr__(self) -> str:
55+
return f"({self.lost}, {self.id})"
56+
# def is_origin(self) -> bool:
57+
# return (self.lost==0) and (self.id==0)
58+
59+
60+
class B_path:
61+
def __init__(self, id:BoardLoc=BoardLoc(0,0), paths:List[Path]=[]) -> None:
62+
self.id=id
63+
# list of paths
64+
self.paths=paths[:]
65+
def __repr__(self) -> str:
66+
return f"<B_path id={self.id} paths={self.paths}>"
67+
68+
# a board class
69+
# a board always has its id but its never stored inside the id
70+
71+
class FeverBoard:
72+
# completely changed in form
73+
def __init__(self, board, last_board:BoardLoc) -> None:
74+
self.board=board
75+
self.last_board=last_board
76+
self.hash=hash(board.tobytes())
77+
78+
def __repr__(self) -> str:
79+
return f"<FeverBoard {self.board} last_board={self.last_board}>"
80+
81+
82+
def get_valid_moves(self, loc:BoardLoc):
83+
# advantages of having a path group
84+
# share same id and reduce the amount of evaluation at a given time
85+
"""returns a list of B_paths which is also a list\n
86+
will be concatnated later\n
87+
ex)\n
88+
[\n
89+
0: [int,int,int]
90+
1: B_path
91+
2: B_path
92+
...\n
93+
]\n
94+
Remember to not append 0 length path values"""
95+
rt = [[]]+[B_path(loc) for x in repeat(None, MAX_INT)]
96+
# 15 elements
97+
# print(rt)
98+
99+
nonzero=np.where(self.board != 0)[0]
100+
if len(nonzero) == 0:
101+
raise Error("ASKED TO FIND PATHS ON AN EMPTY BOARD")
102+
if len(nonzero) == 1:
103+
# there are multiple cases here
104+
if self.board[nonzero[0]] == 1:
105+
# if there is only 1 number
106+
rt[nonzero[0]+1].paths.append(Path(np.byte(nonzero[0]), np.byte(nonzero[0])))
107+
return rt
108+
rt[0].append(nonzero[0])
109+
return rt
110+
for key, select in enumerate(nonzero):
111+
# print("-- select --")
112+
# print(select)
113+
if self.board[select] > 1:
114+
# print("tile appears more than twice. adding paired")
115+
rt[0].append(np.byte(select)) # in c, it would be presented as a single byte
116+
# print("-- target --")
117+
for target in nonzero[key+1:]:
118+
# print(target)
119+
rt[min(target, select)+1].paths.append(Path(np.byte(select), np.byte(target)))
120+
return rt
121+
122+
# methods to do stuff
123+
def copy_do_move_pair_move(self, loc:BoardLoc, sel:int):
124+
"""
125+
this method does a move thats "paired" and returns a new instance
126+
"""
127+
board=copy.copy(self.board)
128+
# board elements are primitives.
129+
# shallow copy will work as well
130+
board[sel]-=2
131+
# change the value
132+
return FeverBoard(board, loc)
133+
def copy_do_move_lost(self, loc:BoardLoc, sel:int, tar:int):
134+
"""
135+
this method does a move where "sel" and "tar" are different and returns a new instance
136+
"""
137+
board=copy.copy(self.board)
138+
if sel == tar:
139+
# the only time it is the same is when the board only has 1 number
140+
# otherwise, its going to be a paired move with lost 0
141+
board[sel]-=1
142+
else:
143+
board[sel]-=1
144+
board[tar]-=1
145+
board[tar-sel-1]+=1
146+
# board elements are primitives.
147+
# shallow copy will work as well
148+
# change the value
149+
return FeverBoard(board, loc)
150+
151+
152+
153+
def __hash__(self):
154+
return self.hash
155+
def __eq__(self, other: object) -> bool:
156+
return self.hash==other.hash
157+
158+
def is_finished(self) -> bool:
159+
return not np.any(self.board, 0)
160+
161+
def format_board(board):
162+
return_board=np.zeros(MAX_INT, numpy.byte) # extremely efficient
163+
for tile in board:
164+
return_board[tile-1]+=1
165+
return return_board
166+
167+
168+
169+
170+
hash_dict={}
171+
# stores hash
172+
# {
173+
# __hash__:BoardLoc
174+
# __hash__:BoardLoc
175+
# }
176+
177+
# used to look up instances
178+
# BoardLoc contains the attribute lost which can be used to evalutate which board is better
179+
180+
181+
global_board_pool=[[] for _ in repeat(None, 30)]
182+
global_path_pool=[[] for _ in repeat(None, 30)]
183+
184+
185+
# we dont evaluate paths until needed
186+
# evalutate boards
187+
# evalutate paths
188+
# repeat
189+
190+
191+
192+
# print("="*100)
193+
original_board=FeverBoard(FeverBoard.format_board([6,10,6,7,15,9,7,3,4,8]), None)
194+
# print(original_board)
195+
global_board_pool[0].append(original_board)
196+
hash_dict[original_board.hash]=BoardLoc(0,0)
197+
198+
199+
# pprint.pprint(global_board_pool)
200+
# pprint.pprint(hash_dict)
201+
202+
# pprint.pprint(FeverBoard(FeverBoard.format_board([3]), None).get_valid_moves(BoardLoc(0,0)))
203+
204+
# new_board=original_board.copy_do_move_pair_move(BoardLoc(0,0), 0)
205+
# new_board=FeverBoard(FeverBoard.format_board([6,10,6,7,15,9,7,3,4,8]), BoardLoc(1,2))
206+
# print(original_board.hash)
207+
# print(new_board.hash)
208+
209+
210+
# exit()
211+
212+
cut=0
213+
def board_eval_recursive(root_board, board_lost):
214+
# print(root_board.hash)
215+
if root_board.is_finished() == True:
216+
print(f"found at cut {cut}")
217+
print("time took")
218+
print(time.time()-start)
219+
return
220+
bl=BoardLoc(board_lost, len(global_board_pool[board_lost]))
221+
if root_board.hash in hash_dict:
222+
if hash_dict[root_board.hash].lost < board_lost:
223+
# raise Error("AN EXISTING HASH DICTIONARY HAD LOWER LOST THAN NEW")
224+
# this statement should also return the funciton
225+
# i just wanted to test some theory
226+
return
227+
if hash_dict[root_board.hash].lost > board_lost:
228+
print("DEBUG: dictionary board had higher lost than new board. replacing...")
229+
global_board_pool[hash_dict[root_board.hash].lost][hash_dict[root_board.hash].id]=None
230+
# set the original board to a compromised board = "None"
231+
# this is a bad practice but memorywise better
232+
hash_dict[root_board.hash]=bl
233+
else:
234+
return
235+
else:
236+
hash_dict[root_board.hash]=bl
237+
# print(bl)
238+
global_board_pool[bl.lost].append(root_board)
239+
moves=root_board.get_valid_moves(bl)
240+
for move in moves[0]:
241+
board_eval_recursive(root_board.copy_do_move_pair_move(bl, move), board_lost)
242+
path_lost=board_lost
243+
for bpath in moves[1:]:
244+
# get the remaining paths
245+
path_lost+=1
246+
if len(bpath.paths) > 0:
247+
global_path_pool[path_lost].append(bpath)
248+
# means we didnt find a solution
249+
return None
250+
def construct_boards(board):
251+
boards=[board]
252+
board=global_board_pool[board.last_board.lost][board.last_board.id]
253+
while board.last_board != None:
254+
boards.append(board)
255+
board=global_board_pool[board.last_board.lost][board.last_board.id]
256+
boards.append(global_board_pool[0][0])
257+
return boards[::-1]
258+
259+
260+
261+
262+
def main():
263+
cut=0
264+
for _ in repeat(None, 20):
265+
# print("----------")
266+
print(f"cut={cut}")
267+
# evaluate all 0 boards
268+
# this shallow copy will allow the board to not get interuppted by the for loop
269+
for id, board in enumerate(global_board_pool[cut][:]):
270+
if board is None: continue
271+
bl=BoardLoc(cut, id)
272+
# print(f"Looping board id {bl}")
273+
moves=board.get_valid_moves(bl)
274+
for paired_move in moves[0]:
275+
board_eval_recursive(board.copy_do_move_pair_move(bl, paired_move), cut)
276+
277+
path_lost=cut
278+
for bpath in moves[1:]:
279+
# get the remaining paths
280+
path_lost+=1
281+
if len(bpath.paths) > 0:
282+
global_path_pool[path_lost].append(bpath)
283+
284+
# print("="*100)
285+
# print("board pool")
286+
# pprint.pprint(global_board_pool)
287+
# print("="*100)
288+
# # print("path pool")
289+
# # pprint.pprint(global_path_pool)
290+
# print("="*100)
291+
# print("dict pool")
292+
# pprint.pprint(hash_dict)
293+
294+
# print("-----")
295+
# print(f"failed to find within cut:{cut}")
296+
cut+=1
297+
# print(f"cut is now {cut}")
298+
# print("generating new boards...")
299+
for bpath in global_path_pool[cut]:
300+
# print(f"evaluating board at loc {bpath.id}")
301+
stem_board=global_board_pool[bpath.id.lost][bpath.id.id]
302+
for p in bpath.paths:
303+
bl=BoardLoc(cut, len(global_board_pool[cut]))
304+
new_board=stem_board.copy_do_move_lost(bpath.id, p.sel, p.tar)
305+
# check hash before adding a board
306+
# probably optimize this in v9
307+
if new_board.is_finished() == True:
308+
print(f"found at cut {cut}")
309+
print("time took")
310+
print(time.time()-start)
311+
return
312+
if new_board.hash in hash_dict:
313+
if hash_dict[new_board.hash].lost < cut+1:
314+
# raise Error("AN EXISTING HASH DICTIONARY HAD LOWER LOST THAN NEW")
315+
# # this statement should also return the funciton
316+
# # i just wanted to test some theory
317+
# do nothing
318+
pass
319+
if hash_dict[new_board.hash].lost > cut+1:
320+
print("DEBUG: dictionary board had higher lost than new board. replacing...")
321+
global_board_pool[hash_dict[new_board.hash].lost][hash_dict[new_board.hash].id]=None
322+
# set the original board to a compromised board = "None"
323+
# this is a bad practice but memorywise better
324+
hash_dict[new_board.hash]=bl
325+
# append because this is a better board
326+
global_board_pool[cut].append(new_board)
327+
# if hash_dict[new_board.hash].lost == new_board:
328+
# print("++++++++++++++++++++++++++")
329+
else:
330+
hash_dict[new_board.hash]=bl
331+
# append if hash was different
332+
global_board_pool[cut].append(new_board)
333+
global_path_pool[cut]=REMOVED_FROM_MEMORY
334+
# print("done adding boards")
335+
# print("="*100)
336+
# print("board pool")
337+
# pprint.pprint(global_board_pool)
338+
# print("="*100)
339+
# print("path pool")
340+
# pprint.pprint(global_path_pool)
341+
# print("="*100)
342+
# print("dict pool")
343+
# pprint.pprint(hash_dict)
344+
main()
345+
main()
346+
main()
347+
main()

0 commit comments

Comments
 (0)