rework logging to log into separate files

This commit is contained in:
Maximilian Keßler 2023-05-20 14:32:42 +02:00
parent 85c9dbd646
commit 746b30dbeb
Signed by: max
GPG key ID: BCC5A619923C0BA5
5 changed files with 99 additions and 39 deletions

View file

@ -1,21 +1,26 @@
import copy import copy
from typing import Tuple, Optional from typing import Tuple, Optional
import verboselogs
from database.database import conn from database.database import conn
from compress import decompress_deck, decompress_actions, compress_actions, link from compress import decompress_deck, decompress_actions, compress_actions, link
from hanabi import Action, GameState from hanabi import Action, GameState
from hanab_live import HanabLiveInstance, HanabLiveGameState from hanab_live import HanabLiveInstance, HanabLiveGameState
from sat import solve_sat from sat import solve_sat
from log_setup import logger, logger_manager
from download_data import detailed_export_game from download_data import detailed_export_game
# returns number of first turn before which the game was infeasible (counting from 0) # returns minimal number T of turns (from game) after which instance was infeasible
# and a replay achieving maximum score (from this turn onwards) if instance is feasible # and a replay achieving maximum score while following the replay for the first (T-1) turns:
# 0 if instance is infeasible # if instance is feasible, returns number of turns + 1
# 1 if instance is feasible but first turn is suboptimal # returns 0 if instance is infeasible
# returns 1 if instance is feasible but first turn is suboptimal
# ... # ...
# (number of turns) if replay was winning # # turns + 1 if the final state is still winning
def check_game(game_id: int) -> Tuple[int, GameState]: def check_game(game_id: int) -> Tuple[int, GameState]:
logger.debug("Analysing game {}".format(game_id))
with conn.cursor() as cur: with conn.cursor() as cur:
cur.execute("SELECT games.num_players, deck, actions, score, games.variant_id FROM games " cur.execute("SELECT games.num_players, deck, actions, score, games.variant_id FROM games "
"INNER JOIN seeds ON seeds.seed = games.seed " "INNER JOIN seeds ON seeds.seed = games.seed "
@ -32,10 +37,13 @@ def check_game(game_id: int) -> Tuple[int, GameState]:
instance = HanabLiveInstance(deck, num_players, variant_id=variant_id) instance = HanabLiveInstance(deck, num_players, variant_id=variant_id)
if instance.max_score == score: if instance.max_score == score:
game = HanabLiveGameState(instance)
for action in actions:
game.make_action(action)
# instance has been won, nothing to compute here # instance has been won, nothing to compute here
return len(actions) return len(actions) + 1, game
# store the turn numbers *before* we know the game was (in)feasible # store lower and upper bounds of numbers of turns after which we know the game was feasible / infeasible
solvable_turn = 0 solvable_turn = 0
unsolvable_turn = len(actions) unsolvable_turn = len(actions)
@ -43,7 +51,9 @@ def check_game(game_id: int) -> Tuple[int, GameState]:
game = HanabLiveGameState(instance) game = HanabLiveGameState(instance)
solvable, solution = solve_sat(game) solvable, solution = solve_sat(game)
if not solvable: if not solvable:
logger.debug("Returning: Instance {} is not feasible")
return 0, solution return 0, solution
logger.verbose("Instance {} is feasible after 0 turns: {}".format(game_id, link(solution)))
while unsolvable_turn - solvable_turn > 1: while unsolvable_turn - solvable_turn > 1:
try_turn = (unsolvable_turn + solvable_turn) // 2 try_turn = (unsolvable_turn + solvable_turn) // 2
@ -51,13 +61,16 @@ def check_game(game_id: int) -> Tuple[int, GameState]:
assert(len(try_game.actions) == solvable_turn) assert(len(try_game.actions) == solvable_turn)
for a in range(solvable_turn, try_turn): for a in range(solvable_turn, try_turn):
try_game.make_action(actions[a]) try_game.make_action(actions[a])
logger.debug("Checking if instance {} is feasible after {} turs".format(game_id, try_turn))
solvable, potential_sol = solve_sat(try_game) solvable, potential_sol = solve_sat(try_game)
if solvable: if solvable:
solution = potential_sol solution = potential_sol
game = try_game game = try_game
solvable_turn = try_turn solvable_turn = try_turn
logger.verbose("Instance {} is feasible after {} turns: {}".format(game_id, solvable_turn, link(solution)))
else: else:
unsolvable_turn = try_turn unsolvable_turn = try_turn
logger.verbose("Instance {} is not feasible after {} turns".format(game_id, unsolvable_turn))
assert(unsolvable_turn - 1 == solvable_turn) assert unsolvable_turn - 1 == solvable_turn, "Programming error"
return unsolvable_turn, solution return unsolvable_turn, solution

View file

@ -1,10 +1,14 @@
#! /usr/bin/python3 #! /usr/bin/python3
import argparse import argparse
import logging
import verboselogs
from check_game import check_game from check_game import check_game
from download_data import detailed_export_game from download_data import detailed_export_game
from compress import link from compress import link
from log_setup import logger, logger_manager
""" """
init db + populate tables init db + populate tables
@ -37,14 +41,15 @@ def add_analyze_subparser(subparsers):
def analyze_game(game_id: int, download: bool = False): def analyze_game(game_id: int, download: bool = False):
if download: if download:
detailed_export_game(game_id) detailed_export_game(game_id)
print('Analyzing game {}'.format(game_id)) logger.info('Analyzing game {}'.format(game_id))
turn, sol = check_game(game_id) turn, sol = check_game(game_id)
if turn == 0: if turn == 0:
print('Instance is unfeasible') logger.info('Instance is unfeasible')
else: else:
print( logger.info('Game was first lost after {} turns.'.format(turn))
'Last winning turn was {}.\n' logger.info(
'A replay achieving perfect score is: {}'.format(turn, link(sol)) 'A replay achieving perfect score from the previous turn onwards is: {}#{}'
.format(link(sol), turn)
) )

View file

@ -1,3 +1,4 @@
from .logger_setup import make_logger from .logger_setup import LoggerManager
logger = make_logger() logger_manager = LoggerManager()
logger = logger_manager.get_logger()

View file

@ -1,20 +1,60 @@
import logging, verboselogs import logging
import verboselogs
def make_logger(): class LoggerManager:
logger = verboselogs.VerboseLogger("hanab-suite") def __init__(self, console_level: int = logging.INFO):
self.logger = verboselogs.VerboseLogger("hanab-suite")
logger.setLevel(logging.DEBUG) self.logger.setLevel(logging.DEBUG)
f_handler = logging.FileHandler("a_log.txt") self.file_formatter = logging.Formatter(
f_formatter = logging.Formatter( '[%(asctime)s] [PID %(process)s] [%(levelname)7s]: %(message)s'
'[%(asctime)s] [%(name)s] [%(levelname)s]: %(message)s' )
)
f_handler.setFormatter(f_formatter)
logger.addHandler(f_handler)
console_handler = logging.StreamHandler() self.info_file_formatter = logging.Formatter(
console_handler.setLevel(logging.INFO) '[%(asctime)s] [PID %(process)s]: %(message)s'
logger.addHandler(console_handler) )
return logger self.console_formatter = logging.Formatter(
'[%(levelname)7s]: %(message)s'
)
self.nothing_formatter = logging.Formatter(
'%(message)s'
)
self.console_handler = logging.StreamHandler()
self.console_handler.setLevel(console_level)
self.console_handler.setFormatter(self.nothing_formatter)
self.debug_file_handler = logging.FileHandler("debug_log.txt")
self.debug_file_handler.setFormatter(self.file_formatter)
self.debug_file_handler.setLevel(logging.DEBUG)
self.verbose_file_handler = logging.FileHandler("verbose_log.txt")
self.verbose_file_handler.setFormatter(self.file_formatter)
self.verbose_file_handler.setLevel(verboselogs.VERBOSE)
self.info_file_handler = logging.FileHandler("log.txt")
self.info_file_handler.setFormatter(self.info_file_formatter)
self.info_file_handler.setLevel(logging.INFO)
self.logger.addHandler(self.console_handler)
self.logger.addHandler(self.debug_file_handler)
self.logger.addHandler(self.verbose_file_handler)
self.logger.addHandler(self.info_file_handler)
def set_console_level(self, level: int):
self.console_handler.setLevel(level)
if level == logging.INFO:
self.console_handler.setFormatter(self.nothing_formatter)
else:
self.console_handler.setFormatter(self.console_formatter)
def is_console_level_active(self, level: int):
return level >= self.console_handler.level
def get_logger(self):
return self.logger

21
sat.py
View file

@ -9,6 +9,7 @@ from hanabi import DeckCard, Action, ActionType, GameState, HanabiInstance
from compress import link, decompress_deck from compress import link, decompress_deck
from greedy_solver import GreedyStrategy from greedy_solver import GreedyStrategy
from constants import COLOR_INITIALS from constants import COLOR_INITIALS
from log_setup import logger
# literals to model game as sat instance to check for feasibility # literals to model game as sat instance to check for feasibility
@ -304,7 +305,7 @@ def solve_sat(starting_state: GameState | HanabiInstance) -> Tuple[bool, Optiona
model = get_model(constraints) model = get_model(constraints)
if model: if model:
print_model(model, game_state, ls) log_model(model, game_state, ls)
solution = evaluate_model(model, copy.deepcopy(game_state), ls) solution = evaluate_model(model, copy.deepcopy(game_state), ls)
return True, solution return True, solution
else: else:
@ -317,21 +318,21 @@ def solve_sat(starting_state: GameState | HanabiInstance) -> Tuple[bool, Optiona
return False, None return False, None
def print_model(model, cur_game_state, ls: Literals): def log_model(model, cur_game_state, ls: Literals):
deck = cur_game_state.deck deck = cur_game_state.deck
first_turn = len(cur_game_state.actions) first_turn = len(cur_game_state.actions)
if first_turn > 0: if first_turn > 0:
print('[print_model] Note: Omitting first {} turns, since they were fixed already.'.format(first_turn)) logger.debug('[print_model] Note: Omitting first {} turns, since they were fixed already.'.format(first_turn))
for m in range(first_turn, cur_game_state.instance.max_winning_moves): for m in range(first_turn, cur_game_state.instance.max_winning_moves):
print('=== move {} ==='.format(m)) logger.debug('=== move {} ==='.format(m))
print('clues: ' + ''.join(str(i) for i in range(1, 9) if model.get_py_value(ls.clues[m][i]))) logger.debug('clues: ' + ''.join(str(i) for i in range(1, 9) if model.get_py_value(ls.clues[m][i])))
print('strikes: ' + ''.join(str(i) for i in range(1, 3) if model.get_py_value(ls.strikes[m][i]))) logger.debug('strikes: ' + ''.join(str(i) for i in range(1, 3) if model.get_py_value(ls.strikes[m][i])))
print('draw: ' + ', '.join('{}: {}'.format(i, deck[i]) for i in range(cur_game_state.progress, cur_game_state.instance.deck_size) if model.get_py_value(ls.draw[m][i]))) logger.debug('draw: ' + ', '.join('{}: {}'.format(i, deck[i]) for i in range(cur_game_state.progress, cur_game_state.instance.deck_size) if model.get_py_value(ls.draw[m][i])))
print('discard: ' + ', '.join('{}: {}'.format(i, deck[i]) for i in range(cur_game_state.instance.deck_size) if model.get_py_value(ls.discard[m][i]))) logger.debug('discard: ' + ', '.join('{}: {}'.format(i, deck[i]) for i in range(cur_game_state.instance.deck_size) if model.get_py_value(ls.discard[m][i])))
for s in range(0, cur_game_state.instance.num_suits): for s in range(0, cur_game_state.instance.num_suits):
print('progress {}: '.format(COLOR_INITIALS[s]) + ''.join(str(r) for r in range(1, 6) if model.get_py_value(ls.progress[m][s, r]))) logger.debug('progress {}: '.format(COLOR_INITIALS[s]) + ''.join(str(r) for r in range(1, 6) if model.get_py_value(ls.progress[m][s, r])))
flags = ['discard_any', 'draw_any', 'play', 'play5', 'incr_clues', 'strike', 'extraround', 'dummyturn'] flags = ['discard_any', 'draw_any', 'play', 'play5', 'incr_clues', 'strike', 'extraround', 'dummyturn']
print(', '.join(f for f in flags if model.get_py_value(getattr(ls, f)[m]))) logger.debug(', '.join(f for f in flags if model.get_py_value(getattr(ls, f)[m])))