1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
|
#-*- coding:utf-8 -*-
# Copyright © 2009-2017 B. Clausius <barcc@gmx.de>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import pickle
from contextlib import suppress
from .model import empty_model
from . import cubestate
from .moves import MoveQueue
class GameState:
def __init__(self):
self.mark_before = False
self.initial_state = cubestate.CubeState(empty_model)
self.initial_state.set_solved()
self.current_state = self.initial_state.copy()
self.move_sequence = MoveQueue()
def copy(self):
game = GameState()
game.mark_before = self.mark_before
game.initial_state = self.initial_state.copy()
game.current_state = self.current_state.copy()
game.move_sequence = self.move_sequence.copy()
return game
def set_state(self, model, blocks, moves, position):
self.initial_state = cubestate.CubeState(model)
try:
self.initial_state.parse_block(blocks)
except ValueError as e:
print('Invalid block list:', *e.args)
print(' start with solved game')
self.initial_state.set_solved()
self.current_state = self.initial_state.copy()
self.move_sequence = MoveQueue()
if moves.startswith('native:'):
moves = moves.split(':', 1)[1].lstrip()
else:
moves = ''
pos, unused_cpos = self.move_sequence.parse(moves, position, model)
self.goto_next_pos(pos)
def get_state(self):
model = self.initial_state.model
blocks = self.initial_state.format_block()
moves, position = self.move_sequence.format(model)
return model, blocks, 'native: ' + moves, position
@staticmethod
def _load_game_file(filename, mtype):
data = None
if filename is not None:
try:
with suppress(FileNotFoundError):
with open(filename, 'rb') as datafile:
data = pickle.load(datafile)
except Exception as e:
print('error while loading game {0}: {1.__class__.__name__}: {1}'.format(mtype, e))
if type(data) is not dict:
data = {}
data_games = None
with suppress(KeyError):
data_games = data['games']
if type(data_games) is not dict:
data_games = {}
data['games'] = data_games
data_type = None
with suppress(KeyError):
data_type = data_games[mtype]
if type(data_type) is not dict:
data_type = {}
data_games[mtype] = data_type
return data, data_type
def load(self, filename, model):
unused_data, data_type = self._load_game_file(filename, model.type)
blocks, moves, position = data_type.get(model.size, ('solved', '', 0))
self.set_state(model, blocks, moves, position)
def save(self, filename):
if filename is None:
return
model, blocks, moves, position = self.get_state()
if model is empty_model:
return
basename = os.path.basename(filename)
dirname = os.path.dirname(filename)
if dirname and not os.path.exists(dirname):
os.makedirs(dirname)
data, data_type = self._load_game_file(filename, model.type)
data_type[model.size] = blocks, moves, position
import tempfile
try:
with tempfile.NamedTemporaryFile('wb', prefix=basename+'-temp-', dir=dirname, delete=False) as datafile:
pickle.dump(data, datafile, protocol=4)
os.rename(datafile.name, filename)
except Exception as e:
print('error while saving game {0}: {1.__class__.__name__}: {1}'.format(model.type, e))
@classmethod
def migrate_1to2(self, filename, mtype, msize, blocks, moves, position):
data, data_type = self._load_game_file(filename, mtype)
data_type[msize] = blocks, moves, position
try:
with open(filename, 'wb') as datafile:
pickle.dump(data, datafile, protocol=4)
except Exception as e:
print('error during migrantion of {0}: {1.__class__.__name__}: {1}'.format(mtype, e))
def random(self, count=-1):
self.initial_state.set_solved()
self.initial_state.random(count)
self.current_state = self.initial_state.copy()
self.move_sequence.reset()
### Funktions to control the cube for use in callbacks and plugins
def step_back(self):
'''One step back in the sequence of moves'''
if self.move_sequence.at_start():
return None
self.mark_before = self.move_sequence.is_mark_current()
self.move_sequence.retard()
move = self.move_sequence.current().inverted()
self.current_state.rotate_slice(move)
return move
def step_next(self):
'''One step forward in the sequence of moves'''
move = self.move_sequence.current()
if move:
self.mark_before = self.move_sequence.is_mark_current()
self.current_state.rotate_slice(move)
self.move_sequence.advance()
return move
def set_next(self, move):
'''Make one new move.'''
self.move_sequence.push_current(move)
def goto_prev_mark(self):
if self.move_sequence.at_start():
return False
while True:
self.move_sequence.retard()
move = self.move_sequence.current().inverted()
self.current_state.rotate_slice(move)
if self.move_sequence.is_mark_current():
break
return True
def goto_start(self):
self.current_state = self.initial_state.copy()
self.move_sequence.rewind_start()
def goto_next_mark(self):
move = self.move_sequence.current()
while move:
self.current_state.rotate_slice(move)
self.move_sequence.advance()
if self.move_sequence.is_mark_current():
break
move = self.move_sequence.current()
def goto_next_pos(self, pos):
move = self.move_sequence.current()
while move:
if pos == self.move_sequence.current_place:
break
self.current_state.rotate_slice(move)
self.move_sequence.advance()
move = self.move_sequence.current()
def set_code(self, code, pos):
self.current_state = self.initial_state.copy()
self.move_sequence.reset()
pos, unused_cpos = self.move_sequence.parse(code, pos, self.current_state.model)
self.goto_next_pos(pos)
def add_moves(self, moves):
for move in moves:
self.current_state.rotate_slice(move)
self.move_sequence.push(move)
def add_code(self, code):
for res in self.move_sequence.parse_iter(code, len(code), self.current_state.model):
self.current_state.rotate_slice(res[0])
def set_plugin_mode(self, plugin_mode):
''' Set plugin mode
append: append moves from the plugin to the game
replace: replace initial state and moves
challenge: replace initial state and clear moves, set the unsolved flag
'''
self.plugin_mode = plugin_mode
def recalc_current_state(self):
self.current_state = self.initial_state.copy()
pos = self.move_sequence.current_place
self.move_sequence.rewind_start()
self.goto_next_pos(pos)
# game transformations
def set_as_initial_state(self):
self.initial_state = self.current_state.copy()
self.move_sequence.truncate_before()
# cube editing
def swap_block(self, blockpos, maxis, mslice, mdir):
self.initial_state.swap_block(blockpos, maxis, mslice, mdir)
self.goto_start()
def rotate_block(self, blockpos, rdir):
self.initial_state.rotate_block(blockpos, rdir)
self.goto_start()
|