-
Notifications
You must be signed in to change notification settings - Fork 2
/
player.py
132 lines (111 loc) · 5.41 KB
/
player.py
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
"""This file creates the players of Qwixx, divides them into subclasses (human, SimpleBot, and AI) and is the place
where decisions are made."""
from board import Board, Row
import numpy as np
from abc import ABC
from copy import deepcopy
class CrossPossibility(object):
"""puts row and eyes of a button into a precise string format"""
def __init__(self, row, eyes):
assert (isinstance(row, Row) or row == 4)
assert (row != 4 or eyes is None)
self.row = row
self.eyes = eyes
def __repr__(self):
return "cp(" + str(self.row) + ", " + str(self.eyes) + ")"
def __eq__(self, other):
return other != "skip" and self.row == other.row and self.eyes == other.eyes
class Player(ABC):
"""makes all decision for doing crosses and informs the player about the state of the boards"""
situation_length = 9
penalty_position = situation_length - 1
def __init__(self, name, ui=None): # !!! remember to reset all variables update start_new_game !!!
self.name = name
# self.opponents = opponents
self.ui = ui
self.board = Board() # own board
# self.others = []
# for opponent_index in range(self.opponents):
# self.others.append(Board()) # list with board of the others # todo #1 avoid duplicated boards
def start_new_game(self) -> None:
"""sets up a new game"""
self.board = Board()
# self.others = []
# for opponent_index in range(self.opponents):
# self.others.append(Board())
def cross_active(self, lst_eyes, valid_turns, completed_lines) -> None:
"""gives UI information about (active) crosses to make"""
if self.ui is None:
return
self.ui.lst_eyes = lst_eyes
self.ui.is_active_player = True
def cross_passive(self, lst_eyes, valid_turns, completed_lines) -> None:
"""gives UI information about (passive) crosses to make"""
if self.ui is None:
return
self.ui.lst_eyes = lst_eyes
self.ui.is_active_player = False
def inform(self, boards, own_index) -> None:
"""informs about boards of all players and updates the knowledge about completed lines/rows"""
self.board = boards[own_index]
self._update_ui()
for player_index in range(len(boards)):
if player_index == own_index:
continue
# if own_index > player_index:
# self.others[player_index] = boards[player_index]
# else:
# self.others[player_index - 1] = boards[player_index]
def inform_about_invalid_turn(self) -> None:
"""informs UI about an invalid turn done by the (human) player"""
assert(self.ui is not None)
self.ui.is_turn_invalid = True
def _update_ui(self) -> None:
"""updates crosses on the UI"""
if self.ui is None:
return
self.ui.penalties = self.board.penalties
self.ui.crosses_by_color = self.board.crosses_by_color
self.ui.show_board()
def show_options(self, possibility_lst) -> None:
"""gives the UI the order to show possible fields to make a cross on"""
if self.ui is None:
return
self.ui.show_options_on_board(possibility_lst)
def _get_current_situation(self):
"""wrapper function to skip computation of hypothetical turns"""
return self._get_hypothetical_situation_after_turns(None, None, [None])
def _get_hypothetical_situation_after_turns(self, completed_lines, is_active_player, turns):
"""creates an numpy array (situation) that describes all boards after turns"""
situation = np.zeros((Player.situation_length,)) # version for longer situation in git history
board = deepcopy(self.board)
for turn in turns: # todo split -> with/without turns
if turn is not None:
assert (is_active_player is not None)
board.cross(turn, completed_lines, is_active_player)
for parameter_type in range(Player.situation_length):
if parameter_type % 2 == 0 and parameter_type != Player.penalty_position:
situation_value = board.row_numbers[parameter_type // 2]
elif parameter_type != Player.penalty_position:
situation_value = board.row_limits[parameter_type // 2]
else:
situation_value = board.penalties
situation[parameter_type] = situation_value
return situation
def get_points(self):
"""calculates current points of a player"""
situation = self._get_current_situation()
player_count = int(len(situation) / Player.situation_length)
player_points = 0
player_index = player_count - 1
# gives points for number of crosses in a row
for row_start in range(0, player_count * Player.penalty_position, player_count * 2): # will be called 4 times
colored_row_number = situation[row_start + player_index]
player_points += (colored_row_number ** 2 + colored_row_number) / 2 # counter(understand only with example): II
# calculates points for penalties
penalty = situation[player_count * Player.penalty_position + player_index]
player_points += penalty * (-5)
return player_points
def end(self, points) -> None:
"""prints the own points"""
print(self.name + f"´s points: {points}")