Sokoban/logic/state.py
2021-03-23 15:38:37 +01:00

325 lines
13 KiB
Python

import config.constants
import typing
import pygame
import logic.player
import logic.keyboardTools
import logic.generate
import logic.rewriter_sok
import solver.solver
import solver.pathNotFoundException
import map.entity_mapper as entity_mapper
import map.mapper as mapper
import interface.goback
class State:
quit = False
next_state = False
state_go_back = False
versus = None
do_generate_levels = False
dicGen = {"1": [8, 8, 2, 3, 1], "2": [9, 9, 3, 4, 2], "3": [10, 10, 4, 5, 2], "4": [15, 15, 6, 7, 2], "5": [
17, 17, 7, 8, 2], "6": [18, 18, 8, 9, 1], "7": [20, 20, 6, 7, 2], "8": [25, 25, 7, 8, 3], "9": [30, 30, 7, 10, 4]}
difficulty: typing.Union[str, None] = None
level: typing.Union[int, str, None] = None
players: typing.List[logic.player.Player] = []
situations = ('select_difficult', 'select_level', 'select_versus_mode', 'game')
situation = 0
inputs = {
"option_menu_difficulty": logic.keyboardTools.KeyboardTools.get_keys_event_from_choices(['g']),
"difficulty": logic.keyboardTools.KeyboardTools.get_keys_event_from_choices(['a', 'b', 'c', 'e']),
"levels": {
'a': logic.keyboardTools.KeyboardTools.get_keys_event_from_range(1, 9),
'b': logic.keyboardTools.KeyboardTools.get_keys_event_from_range(1, 3),
'c': logic.keyboardTools.KeyboardTools.get_keys_event_from_range(4, 6),
'e': logic.keyboardTools.KeyboardTools.get_keys_event_from_range(7, 9)
},
"versus": logic.keyboardTools.KeyboardTools.get_keys_event_from_choices(['a', 'b']),
}
@classmethod
def inputs_checker(cls, event, valid_inputs):
"""Check if the key the player press is valid or not"""
for valid_input in valid_inputs:
if event.key == getattr(pygame, valid_input) \
or (getattr(event, "unicode", None) and event.unicode.lower() == logic.keyboardTools.KeyboardTools.get_str_from_keyboard_event(valid_input).lower()):
return True
return False
@classmethod
def analizer(cls, events: list, textes=None):
"""Analize the events and modify the state class's variables in consequence"""
for event in events:
if event.type == pygame.QUIT:
cls.quit = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
cls.state_go_back = True
else:
if not cls.analizer_menu(event):
cls.analizer_game(event)
elif event.type == pygame.MOUSEBUTTONUP:
cls.analizer_menu_mouse(event, textes)
elif event.type == config.constants.COMPUTER_EVENT:
cls.analizer_game_from_computer()
@classmethod
def get_letter_pointed_from_mouse(cls, event, textes, attr):
"""Change the variables "state_go_back" and "next_state" according to what the mouse is pointing at"""
for texte in textes:
if texte.rect.collidepoint(event.pos):
if isinstance(texte, interface.goback.GoBack):
cls.state_go_back = True
break
elif ':' in texte.texte:
command = texte.texte.split(":")[0].lower().strip()
command_pygame_key = logic.keyboardTools.KeyboardTools.get_keys_event_from_choices([command])[0]
if command_pygame_key in cls.inputs["option_menu_difficulty"]:
cls.do_generate_levels = True
else:
setattr(cls, attr, command)
cls.next_state = True
break
@classmethod
def analizer_menu_mouse(cls, event, textes):
"""Enable the player to choose a level and a game difficulty in the menu with the mouse pointer"""
if cls.situations[cls.situation][:6] != "select":
return False
if cls.difficulty is None:
cls.get_letter_pointed_from_mouse(event, textes, "difficulty")
elif cls.level is None:
for texte in textes:
if texte.rect.collidepoint(event.pos):
if isinstance(texte, interface.goback.GoBack):
cls.state_go_back = True
break
elif ':' in texte.texte:
try:
integer = int(texte.texte.split(":")[0].strip().lower()[-1])
cls.level = integer
cls.next_state = True
except ValueError:
pass
except IndexError:
pass
elif cls.versus is None:
cls.get_letter_pointed_from_mouse(event, textes, "versus")
return True
@classmethod
def get_unicode_letter_for_situation(cls, event, input_sector, attr):
"""Return the unicode of a given letter and make the 'next_state' variable True """
if cls.inputs_checker(event, cls.inputs[input_sector]):
integer = logic.keyboardTools.KeyboardTools.check_integer_keyboard(
event.unicode)
if integer is None:
if event.unicode.isalpha():
setattr(cls, attr, event.unicode.lower())
cls.next_state = True
@classmethod
def analizer_menu(cls, event):
"""Movidifed the State class considering what the player choose in the menu interface"""
if cls.situations[cls.situation][:6] != "select":
return False
if cls.difficulty is None:
if cls.inputs_checker(event, cls.inputs["option_menu_difficulty"]):
cls.do_generate_levels = True
else:
cls.get_unicode_letter_for_situation(event, "difficulty", "difficulty")
elif cls.level is None:
if cls.inputs_checker(event, cls.inputs['levels'][cls.difficulty]):
integer = logic.keyboardTools.KeyboardTools.check_integer_keyboard(
event.unicode)
if integer is not None:
cls.level = integer
cls.next_state = True
elif cls.versus is None:
cls.get_unicode_letter_for_situation(event, "versus", "versus")
return True
@classmethod
def generate_grids_for_levels(cls):
"""Call the Generate class to create a random level according to the parameters we
decided for each level difficulty (in the dicGen dictionnary)"""
i = 1
while i <= 9:
grid = logic.generate.Generate.gridGen(
cls.dicGen[str(i)][0],
cls.dicGen[str(i)][1],
cls.dicGen[str(i)][2],
cls.dicGen[str(i)][3],
cls.dicGen[str(i)][4])
_mapper = mapper.Mapper("", grid)
_player = logic.player.Player(entity_mapper.EntityMapper(_mapper, i, initsprites=False))
_solver = solver.solver.Solver(_player)
try:
_solver.get_full_path()
yield grid
except solver.pathNotFoundException.Path_not_found_exception:
i -= 1
i += 1
@classmethod
def generate_levels(cls):
"""Call the Overwrite class to erase all the levels existing and replace them by random generated levels"""
overwriter = logic.rewriter_sok.Overwrite()
grids = [grid for grid in cls.generate_grids_for_levels()]
for i, grid in enumerate(grids, start=1):
overwriter.writer(grid, str(i))
cls.do_generate_levels = False
@classmethod
def analizer_game(cls, event):
"""This fonction analize all the player's actions during a game and act consequently"""
if cls.situations[cls.situation] != "game":
return False
for player in cls.players:
cls.game_traitment(player)
if player.inputs:
valid_inputs = logic.keyboardTools.KeyboardTools.get_keys_event_from_choices(
player.inputs)
if cls.inputs_checker(event, valid_inputs):
player.player_analizer(event)
if cls.versus == "a" and event.key == pygame.K_o and player.entity_mapper.position == 1:
player.restart_player_game()
if cls.versus == "a" and any((player.had_moved for player in cls.players)):
cls.analizer_game_from_computer()
if cls.players[0].interrupt and not cls.players[1].interrupt:
pygame.time.set_timer(config.constants.COMPUTER_EVENT, 500)
if all((player.interrupt for player in cls.players)):
cls.next_state = True
return True
@classmethod
def analizer_game_from_computer(cls):
"""All the computer to finish the game alone if the player beats him"""
if cls.situations[cls.situation] != "game":
return False
cls.game_traitment(cls.players[1])
cls.players[1].move_from_full_path()
if cls.players[0].interrupt:
if all((player.interrupt for player in cls.players)):
cls.next_state = True
return True
@classmethod
def go_back(cls):
"""Make the game one step back """
cls.leave_situation(-1)
if cls.situation < 0:
cls.situation = 0
cls.quit = True
if not cls.situations[cls.situation] == "select_versus_mode":
cls.state_go_back = False
@classmethod
def leave_situation(cls, direction):
"""Change the situation variable 'situation' to change the 'cls.situations[cls.situations]' value.
Also impact the 'level', 'difficulty', and 'versus' vraibles"""
if cls.situations[cls.situation] == "select_level":
if direction == -1:
cls.level = None
cls.difficulty = None
elif cls.situations[cls.situation] == "select_versus_mode":
if direction == -1:
cls.versus = None
cls.level = None
elif cls.situations[cls.situation] == "game":
for player in cls.players:
player.entity_mapper.sprites.empty()
cls.players.clear()
if cls.versus == "a":
pygame.time.set_timer(config.constants.COMPUTER_EVENT, 0)
if direction == 1:
cls.level = None
cls.difficulty = None
cls.versus = None
if direction == -1:
cls.situation -= 1
else:
cls.situation = (cls.situation + 1) % len(cls.situations)
@classmethod
def generate_players(cls):
""""""
for player in cls.players:
player.entity_mapper.sprites.empty()
cls.players.clear()
if cls.difficulty == "a" and cls.level is not None:
grid = logic.generate.Generate.gridGen(
cls.dicGen[str(cls.level)][0],
cls.dicGen[str(cls.level)][1],
cls.dicGen[str(cls.level)][2],
cls.dicGen[str(cls.level)][3],
cls.dicGen[str(cls.level)][4]
)
_mapper = mapper.Mapper("", grid)
elif cls.level is not None:
_mapper = mapper.Mapper("level" + str(cls.level) + ".sok")
if cls.level is not None:
for i in range(2):
_player = logic.player.Player(
entity_mapper.EntityMapper(_mapper, i))
cls.players.append(_player)
@classmethod
def select_game_traitment(cls):
""""""
cls.generate_players()
if cls.versus == "a" and cls.players:
while True:
cls.players[1].inputs = []
_solver = solver.solver.Solver(cls.players[1])
if cls.difficulty == "a":
try:
_solver.get_full_path()
break
except solver.pathNotFoundException.Path_not_found_exception:
cls.generate_players()
continue
else:
try:
_solver.get_full_path()
break
except solver.pathNotFoundException.Path_not_found_exception:
cls.next_state = True
cls.next_state = True
@classmethod
def game_traitment(cls, player):
"""Reset the player statuts"""
if player.restarted:
player.restarted = False
if player.full_path_temoin:
player.full_path = player.full_path_temoin[:]
player.reset_status()