2023-05-06 09:06:01 +02:00
from sat import solve_sat
2023-05-14 19:11:10 +02:00
from database import conn
2023-03-02 22:16:43 +01:00
from download_data import export_game
2023-05-14 19:11:10 +02:00
from variants import VARIANTS , variant_name
2023-03-13 11:46:23 +01:00
from alive_progress import alive_bar
2023-03-15 15:45:22 +01:00
from compress import decompress_deck , link
2023-03-14 00:31:21 +01:00
import concurrent . futures
2023-03-13 23:47:59 +01:00
from threading import Lock
2023-05-14 19:11:10 +02:00
from time import perf_counter
2023-03-15 15:45:22 +01:00
from greedy_solver import GameState , GreedyStrategy
2023-05-14 19:11:10 +02:00
from log_setup . logger_setup import logger
from deck_analyzer import analyze , InfeasibilityReason
2023-03-02 11:04:45 +01:00
2023-03-14 09:14:14 +01:00
MAX_PROCESSES = 4
2023-03-14 00:31:21 +01:00
2023-03-02 22:16:43 +01:00
def update_seeds_db ( ) :
cur2 = conn . cursor ( )
with conn . cursor ( ) as cur :
cur . execute ( " SELECT num_players, seed, variant_id from games; " )
for ( num_players , seed , variant_id ) in cur :
cur2 . execute ( " SELECT COUNT(*) from seeds WHERE seed = ( %s ); " , ( seed , ) )
if cur2 . fetchone ( ) [ 0 ] == 0 :
print ( " new seed {} " . format ( seed ) )
cur2 . execute ( " INSERT INTO seeds "
" (seed, num_players, variant_id) "
" VALUES "
" ( %s , %s , %s ) " ,
( seed , num_players , variant_id )
)
conn . commit ( )
else :
print ( " seed {} already found in DB " . format ( seed ) )
2023-03-02 11:04:45 +01:00
2023-03-02 22:16:43 +01:00
def get_decks_of_seeds ( ) :
cur = conn . cursor ( )
cur2 = conn . cursor ( )
cur . execute ( " SELECT seed FROM seeds WHERE deck is NULL " )
for ( seed , ) in cur :
cur2 . execute ( " SELECT id FROM games WHERE seed = ( %s ) " , ( seed , ) )
( game_id , ) = cur2 . fetchone ( )
print ( " Exporting game {} for seed {} . " . format ( game_id , seed ) )
export_game ( game_id )
conn . commit ( )
2023-03-02 11:04:45 +01:00
2023-03-13 11:46:23 +01:00
def update_trivially_feasible_games ( ) :
cur = conn . cursor ( )
for var in VARIANTS :
2023-03-13 13:22:54 +01:00
cur . execute ( " SELECT seed FROM seeds WHERE variant_id = ( %s ) AND feasible is null " , ( var [ ' id ' ] , ) )
seeds = cur . fetchall ( )
print ( ' Checking variant {} (id {} ), found {} seeds to check... ' . format ( var [ ' name ' ] , var [ ' id ' ] , len ( seeds ) ) )
with alive_bar ( total = len ( seeds ) , title = ' {} ( {} ) ' . format ( var [ ' name ' ] , var [ ' id ' ] ) ) as bar :
for ( seed , ) in seeds :
cur . execute ( " SELECT id, deck_plays, one_extra_card, one_less_card, all_or_nothing "
" FROM games WHERE score = ( %s ) AND seed = ( %s ) ORDER BY id; " ,
( 5 * len ( var [ ' suits ' ] ) , seed )
)
res = cur . fetchall ( )
print ( " Checking seed {} : {:3} results " . format ( seed , len ( res ) ) )
for ( game_id , a , b , c , d ) in res :
if None in [ a , b , c , d ] :
print ( ' Game {} not found in database, exporting... ' . format ( game_id ) )
succ , valid = export_game ( game_id )
if not succ :
print ( ' Error exporting game {} . ' . format ( game_id ) )
continue
else :
valid = not any ( [ a , b , c , d ] )
print ( ' Game {} already in database, valid: {} ' . format ( game_id , valid ) )
if valid :
2023-03-14 09:14:14 +01:00
print ( ' Seed {:10} (variant {} / {} ) found to be feasible via game {:6} ' . format ( seed , var [ ' id ' ] , var [ ' name ' ] , game_id ) )
2023-03-13 13:22:54 +01:00
cur . execute ( " UPDATE seeds SET feasible = ( %s ) WHERE seed = ( %s ) " , ( True , seed ) )
conn . commit ( )
break
else :
print ( ' Cheaty game found ' )
bar ( )
2023-03-13 11:46:23 +01:00
2023-03-02 13:07:53 +01:00
2023-03-13 23:47:59 +01:00
def get_decks_for_all_seeds ( ) :
cur = conn . cursor ( )
cur . execute ( " SELECT id "
" FROM games "
" INNER JOIN seeds "
" ON seeds.seed = games.seed "
" WHERE "
" seeds.deck is null "
" AND "
" games.id = ( "
" SELECT id FROM games WHERE games.seed = seeds.seed LIMIT 1 "
" ) "
)
print ( " Exporting decks for all seeds " )
res = cur . fetchall ( )
with alive_bar ( len ( res ) , title = " Exporting decks " ) as bar :
for ( game_id , ) in res :
export_game ( game_id )
bar ( )
2023-03-14 00:31:21 +01:00
mutex = Lock ( )
2023-03-15 15:45:22 +01:00
def solve_instance ( num_players , deck ) :
2023-03-18 01:17:32 +01:00
# first, sanity check on running out of pace
result = analyze ( deck , num_players )
if result is not None :
assert type ( result ) == InfeasibilityReason
logger . info ( " found infeasible deck " )
return False , None , None
2023-03-15 15:45:22 +01:00
for num_remaining_cards in [ 0 , 5 , 10 , 20 , 30 ] :
2023-03-18 01:17:32 +01:00
# logger.info("trying with {} remaining cards".format(num_remaining_cards))
2023-03-15 15:45:22 +01:00
game = GameState ( num_players , deck )
strat = GreedyStrategy ( game )
# make a number of greedy moves
while not game . is_over ( ) and not game . is_known_lost ( ) :
if num_remaining_cards != 0 and game . progress == game . deck_size - num_remaining_cards :
break # stop solution here
strat . make_move ( )
# check if we won already
if game . is_won ( ) :
# print("won with greedy strat")
return True , game , num_remaining_cards
# now, apply sat solver
if not game . is_over ( ) :
2023-03-18 01:17:32 +01:00
logger . info ( " continuing greedy sol with SAT " )
2023-03-15 15:45:22 +01:00
solvable , sol = solve_sat ( game )
if solvable :
2023-03-18 01:17:32 +01:00
return True , sol , num_remaining_cards
logger . info ( " No success with {} remaining cards, reducing number of greedy moves, failed attempt was: {} " . format ( num_remaining_cards , link ( game . to_json ( ) ) ) )
2023-03-15 15:45:22 +01:00
# print("Aborting trying with greedy strat")
logger . info ( " Starting full SAT solver " )
game = GameState ( num_players , deck )
a , b = solve_sat ( game )
return a , b , 99
2023-03-14 09:14:14 +01:00
def solve_seed ( seed , num_players , deck_compressed , var_id ) :
2023-05-06 09:06:01 +02:00
try :
deck = decompress_deck ( deck_compressed )
t0 = perf_counter ( )
solvable , solution , num_remaining_cards = solve_instance ( num_players , deck )
t1 = perf_counter ( )
logger . info ( " Solved instance {} in {} seconds " . format ( seed , round ( t1 - t0 , 2 ) ) )
mutex . acquire ( )
if solvable is not None :
lcur = conn . cursor ( )
lcur . execute ( " UPDATE seeds SET feasible = ( %s ) WHERE seed = ( %s ) " , ( solvable , seed ) )
conn . commit ( )
if solvable == True :
with open ( " remaining_cards.txt " , " a " ) as f :
f . write ( " Success with {} cards left in draw by greedy solver on seed {} : {} \n " . format ( num_remaining_cards , seed , link ( solution . to_json ( ) ) ) )
elif solvable == False :
logger . info ( " seed {} was not solvable " . format ( seed ) )
with open ( ' infeasible_instances.txt ' , ' a ' ) as f :
f . write ( ' {} -player, seed {:10} , {} \n ' . format ( num_players , seed , variant_name ( var_id ) ) )
elif solvable is None :
logger . info ( " seed {} skipped " . format ( seed ) )
else :
raise Exception ( " Programming Error " )
mutex . release ( )
except Exception :
traceback . format_exc ( )
print ( " exception in subprocess: " )
2023-03-13 23:47:59 +01:00
2023-03-13 11:46:23 +01:00
def solve_unknown_seeds ( ) :
2023-03-13 23:47:59 +01:00
cur = conn . cursor ( )
2023-03-13 11:46:23 +01:00
for var in VARIANTS :
2023-03-15 15:45:22 +01:00
cur . execute ( " SELECT seed, num_players, deck FROM seeds WHERE variant_id = ( %s ) AND feasible IS NULL AND deck IS NOT NULL " , ( var [ ' id ' ] , ) )
2023-03-13 23:47:59 +01:00
res = cur . fetchall ( )
2023-03-14 00:31:21 +01:00
2023-03-18 01:17:32 +01:00
# for r in res:
# solve_seed(r[0], r[1], r[2], var['id'])
2023-03-15 15:45:22 +01:00
with concurrent . futures . ProcessPoolExecutor ( max_workers = MAX_PROCESSES ) as executor :
2023-03-14 09:14:14 +01:00
fs = [ executor . submit ( solve_seed , r [ 0 ] , r [ 1 ] , r [ 2 ] , var [ ' id ' ] ) for r in res ]
2023-03-14 00:31:21 +01:00
with alive_bar ( len ( res ) , title = ' Seed solving on {} ' . format ( var [ ' name ' ] ) ) as bar :
for f in concurrent . futures . as_completed ( fs ) :
bar ( )
2023-03-13 23:47:59 +01:00
break
solve_unknown_seeds ( )