2019-03-14 00:01:02 +01:00
|
|
|
|
|
|
|
import numpy as np
|
|
|
|
import json
|
|
|
|
import base64
|
|
|
|
|
2019-03-27 17:23:04 +01:00
|
|
|
from tools import debug
|
|
|
|
|
2019-04-01 16:13:44 +02:00
|
|
|
from database_connection import get_sql_time
|
|
|
|
|
2019-03-14 00:01:02 +01:00
|
|
|
# decoder/encoder took from: https://stackoverflow.com/a/19271311
|
|
|
|
|
|
|
|
|
|
|
|
def Base64Encode(ndarray):
|
|
|
|
return json.dumps([str(ndarray.dtype), base64.b64encode(ndarray), ndarray.shape])
|
|
|
|
|
|
|
|
|
|
|
|
def Base64Decode(jsonDump):
|
|
|
|
loaded = json.loads(jsonDump)
|
|
|
|
dtype = np.dtype(loaded[0])
|
|
|
|
arr = np.frombuffer(base64.decodestring(loaded[1]), dtype)
|
|
|
|
if len(loaded) > 2:
|
|
|
|
return arr.reshape(loaded[2])
|
|
|
|
return arr
|
|
|
|
|
|
|
|
|
|
|
|
def SimpleEncode(ndarray):
|
|
|
|
return json.dumps(ndarray.tolist())
|
|
|
|
|
|
|
|
|
|
|
|
def SimpleDecode(jsonDump):
|
|
|
|
return np.array(json.loads(jsonDump))
|
|
|
|
|
|
|
|
|
2019-03-29 12:52:03 +01:00
|
|
|
class Match(object):
|
2019-03-14 00:01:02 +01:00
|
|
|
|
2019-03-29 12:52:03 +01:00
|
|
|
FIELD_EMPTY = 0
|
|
|
|
FIELD_USER_A = 1
|
|
|
|
FIELD_USER_B = 2
|
|
|
|
FIELD_DRAW = 3
|
2019-03-14 00:01:02 +01:00
|
|
|
|
2019-04-04 13:44:48 +02:00
|
|
|
def __init__(self, n, match_id, revoke_time, player_a_name, player_b_name, json_state=None, ranked=False):
|
2019-03-14 00:01:02 +01:00
|
|
|
self.n = n
|
|
|
|
self.id = match_id
|
|
|
|
self.complete_field = np.zeros(shape=(n*n, n*n), dtype=int)
|
|
|
|
self.global_field = np.zeros(shape=(n, n), dtype=int)
|
|
|
|
self.player_won = None
|
2019-03-29 12:52:03 +01:00
|
|
|
self.is_draw = False
|
2019-03-14 00:01:02 +01:00
|
|
|
self.game_over = False
|
|
|
|
self.last_move = None
|
|
|
|
self.is_player_a = True
|
|
|
|
self.player_a_name = player_a_name
|
|
|
|
self.player_b_name = player_b_name
|
2019-04-01 16:13:44 +02:00
|
|
|
self.revoke_time = revoke_time
|
2019-04-04 13:44:48 +02:00
|
|
|
self.ranked = ranked
|
2019-03-14 00:01:02 +01:00
|
|
|
|
|
|
|
if json_state is not None:
|
|
|
|
self.from_json_state(json_state)
|
2019-04-01 16:13:44 +02:00
|
|
|
|
|
|
|
def get_sql_revoke_time(self):
|
|
|
|
return get_sql_time(self.revoke_time)
|
2019-03-14 00:01:02 +01:00
|
|
|
|
|
|
|
def from_json_state(self, json_state):
|
|
|
|
match_obj = json.loads(json_state)
|
|
|
|
self.complete_field = np.array(match_obj['complete_field'], dtype=int)
|
|
|
|
self.global_field = np.array(match_obj['global_field'], dtype=int)
|
|
|
|
self.player_won = match_obj['player_won']
|
|
|
|
self.game_over = match_obj['game_over']
|
|
|
|
self.last_move = match_obj['last_move']
|
|
|
|
self.is_player_a = match_obj['active_player'] == self.player_a_name
|
2019-03-29 12:52:03 +01:00
|
|
|
|
|
|
|
# draw state w.r.t backward compability
|
|
|
|
self.is_draw = match_obj['is_draw'] if 'is_draw' in match_obj else False
|
|
|
|
|
2019-03-14 00:01:02 +01:00
|
|
|
def to_json_state(self):
|
2019-03-29 12:52:03 +01:00
|
|
|
return json.dumps(self.to_dict_state())
|
|
|
|
|
|
|
|
def to_dict_state(self):
|
|
|
|
return {
|
2019-03-14 00:01:02 +01:00
|
|
|
'complete_field': self.complete_field.tolist(),
|
|
|
|
'global_field': self.global_field.tolist(),
|
|
|
|
'last_move': self.last_move,
|
|
|
|
'game_over': self.game_over,
|
|
|
|
'player_won': self.player_won,
|
2019-03-21 12:06:22 +01:00
|
|
|
'active_player': self.player_a_name if self.is_player_a else self.player_b_name,
|
|
|
|
'player_a': self.player_a_name,
|
2019-03-29 12:52:03 +01:00
|
|
|
'player_b': self.player_b_name,
|
|
|
|
'is_draw': self.is_draw
|
2019-03-14 00:01:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
def switch_player_names(self):
|
|
|
|
tmp = self.player_a_name
|
|
|
|
self.player_a_name = self.player_b_name
|
|
|
|
self.player_b_name = tmp
|
|
|
|
|
|
|
|
def get_current_player(self):
|
|
|
|
return self.player_a_name if self.is_player_a else self.player_b_name
|
|
|
|
|
|
|
|
def is_move_valid(self, sub_x, sub_y, x, y):
|
|
|
|
if sub_x < 0 or sub_x >= self.n:
|
|
|
|
return False
|
|
|
|
if sub_y < 0 or sub_y >= self.n:
|
|
|
|
return False
|
|
|
|
|
|
|
|
if x < 0 or x >= self.n:
|
|
|
|
return False
|
|
|
|
if y < 0 or y >= self.n:
|
|
|
|
return False
|
|
|
|
|
|
|
|
if (self.last_move is not None):
|
|
|
|
last_x = self.last_move['x']
|
|
|
|
last_y = self.last_move['y']
|
|
|
|
last_sub_x = self.last_move['sub_x']
|
|
|
|
last_sub_y = self.last_move['sub_y']
|
|
|
|
|
2019-03-29 12:52:03 +01:00
|
|
|
if sub_x != last_x and self.global_field[last_y, last_x] == Match.FIELD_EMPTY:
|
2019-03-14 00:01:02 +01:00
|
|
|
# user is not allowed to place everywhere! wrong move!
|
|
|
|
return False
|
|
|
|
|
2019-03-29 12:52:03 +01:00
|
|
|
if sub_y != last_y and self.global_field[last_y, last_x] == Match.FIELD_EMPTY:
|
2019-03-14 00:01:02 +01:00
|
|
|
return False
|
|
|
|
|
2019-03-29 12:52:03 +01:00
|
|
|
if self.complete_field[sub_y * self.n + y][sub_x * self.n + x] != Match.FIELD_EMPTY:
|
2019-03-14 00:01:02 +01:00
|
|
|
return False
|
2019-03-29 12:52:03 +01:00
|
|
|
|
2019-03-21 12:06:22 +01:00
|
|
|
return True
|
2019-03-14 00:01:02 +01:00
|
|
|
|
|
|
|
def is_full(self, field):
|
2019-03-29 12:52:03 +01:00
|
|
|
return not field.__contains__(Match.FIELD_EMPTY)
|
2019-03-14 00:01:02 +01:00
|
|
|
|
|
|
|
def check_win(self, field, x, y):
|
|
|
|
is_col = True
|
|
|
|
is_row = True
|
|
|
|
is_main_diag = False
|
|
|
|
is_sec_diag = False
|
|
|
|
|
|
|
|
val = field[y, x]
|
|
|
|
|
|
|
|
for i in range(self.n):
|
|
|
|
if (field[i, x] != val):
|
|
|
|
is_col = False
|
|
|
|
break
|
|
|
|
|
|
|
|
for i in range(self.n):
|
|
|
|
if (field[y, i] != val):
|
|
|
|
is_row = False
|
|
|
|
break
|
|
|
|
|
|
|
|
if x == y:
|
|
|
|
is_main_diag = True
|
|
|
|
for i in range(self.n):
|
|
|
|
if field[i, i] != val:
|
|
|
|
is_main_diag = False
|
|
|
|
break
|
|
|
|
|
|
|
|
if x + y == self.n - 1:
|
|
|
|
is_sec_diag = True
|
|
|
|
for i in range(self.n):
|
2019-03-21 12:06:22 +01:00
|
|
|
if field[i, self.n - i - 1] != val:
|
2019-03-14 00:01:02 +01:00
|
|
|
is_sec_diag = False
|
|
|
|
break
|
|
|
|
|
|
|
|
return is_col or is_row or is_main_diag or is_sec_diag
|
|
|
|
|
|
|
|
def move(self, move_dict):
|
2019-04-01 16:13:44 +02:00
|
|
|
if self.game_over:
|
|
|
|
debug("invalid move")
|
|
|
|
return False
|
|
|
|
|
2019-03-21 12:06:22 +01:00
|
|
|
sub_x = int(move_dict['sub_x'])
|
|
|
|
sub_y = int(move_dict['sub_y'])
|
|
|
|
x = int(move_dict['x'])
|
|
|
|
y = int(move_dict['y'])
|
2019-03-14 00:01:02 +01:00
|
|
|
|
|
|
|
abs_x = sub_x * self.n + x
|
|
|
|
abs_y = sub_y * self.n + y
|
|
|
|
|
2019-03-29 12:52:03 +01:00
|
|
|
player_mark = Match.FIELD_USER_A if self.is_player_a else Match.FIELD_USER_B
|
2019-03-14 00:01:02 +01:00
|
|
|
|
|
|
|
if not self.is_move_valid(sub_x, sub_y, x, y):
|
2019-03-27 17:23:04 +01:00
|
|
|
debug("invalid move")
|
2019-03-14 00:01:02 +01:00
|
|
|
return False
|
|
|
|
|
|
|
|
# else: move!
|
|
|
|
self.complete_field[abs_y, abs_x] = player_mark
|
|
|
|
|
|
|
|
# encode move:
|
2019-03-21 12:06:22 +01:00
|
|
|
self.last_move = {'sub_x': sub_x, 'sub_y': sub_y, 'x': x, 'y': y}
|
2019-03-14 00:01:02 +01:00
|
|
|
|
|
|
|
# check whether this indicates changes in the global field:
|
2019-03-29 12:52:03 +01:00
|
|
|
if self.global_field[sub_y, sub_x] != Match.FIELD_EMPTY:
|
2019-03-27 17:23:04 +01:00
|
|
|
debug("field not empty")
|
2019-03-21 12:06:22 +01:00
|
|
|
return False
|
2019-03-14 00:01:02 +01:00
|
|
|
|
|
|
|
subgrid = self.complete_field[sub_y * self.n: (
|
|
|
|
sub_y + 1) * self.n, sub_x * self.n: (sub_x + 1) * self.n]
|
|
|
|
|
|
|
|
if self.check_win(subgrid, x, y):
|
2019-03-21 12:06:22 +01:00
|
|
|
self.global_field[sub_y, sub_x] = player_mark
|
|
|
|
if self.check_win(self.global_field, sub_x, sub_y):
|
|
|
|
self.game_over = True
|
|
|
|
self.player_won = self.player_a_name if self.is_player_a else self.player_b_name
|
2019-03-14 00:01:02 +01:00
|
|
|
|
|
|
|
elif self.is_full(subgrid):
|
2019-03-29 12:52:03 +01:00
|
|
|
self.global_field[sub_y, sub_x] = Match.FIELD_DRAW
|
2019-03-21 12:06:22 +01:00
|
|
|
if self.is_full(self.global_field):
|
|
|
|
self.game_over = True
|
|
|
|
self.player_won = None
|
2019-03-29 12:52:03 +01:00
|
|
|
self.is_draw = True
|
2019-03-14 00:01:02 +01:00
|
|
|
|
|
|
|
self.is_player_a = not self.is_player_a
|
|
|
|
|
|
|
|
return True
|