software_challenge_client 21.2.0 → 22.1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +4 -1
- data/Dockerfile +1 -1
- data/README.md +1 -1
- data/RELEASES.md +8 -0
- data/bin/console +0 -0
- data/bin/setup +0 -0
- data/develop.sh +0 -0
- data/example/main.rb +0 -0
- data/example/start.bat +0 -0
- data/generate-authors.sh +0 -0
- data/lib/software_challenge_client/board.rb +11 -61
- data/lib/software_challenge_client/color.rb +13 -3
- data/lib/software_challenge_client/field.rb +30 -11
- data/lib/software_challenge_client/game_rule_logic.rb +61 -254
- data/lib/software_challenge_client/game_state.rb +153 -232
- data/lib/software_challenge_client/move.rb +41 -0
- data/lib/software_challenge_client/piece.rb +55 -63
- data/lib/software_challenge_client/piece_type.rb +16 -0
- data/lib/software_challenge_client/player.rb +12 -6
- data/lib/software_challenge_client/protocol.rb +201 -266
- data/lib/software_challenge_client/runner.rb +1 -1
- data/lib/software_challenge_client/team.rb +25 -0
- data/lib/software_challenge_client/util/constants.rb +2 -3
- data/lib/software_challenge_client/version.rb +1 -1
- data/lib/software_challenge_client.rb +3 -6
- data/lib/update_client_module.sh +0 -0
- data/push_image_production.sh +12 -0
- data/release.sh +0 -0
- metadata +7 -10
- data/lib/software_challenge_client/coordinate_set.rb +0 -92
- data/lib/software_challenge_client/piece_shape.rb +0 -109
- data/lib/software_challenge_client/player_type.rb +0 -14
- data/lib/software_challenge_client/rotation.rb +0 -22
- data/lib/software_challenge_client/set_move.rb +0 -24
- data/lib/software_challenge_client/skip_move.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fb51cb43ad01dbd89c1201d2cfc724131e479c4046421f74f7c437c8a97572ea
|
4
|
+
data.tar.gz: 35209a8eee036071d32ad0d397eb0c79d91e3a99785846b2171867687fe048bd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c363296bfad49e86c6b5394564e1460b50e09899ab673da822497e88b04b4236618767a1ca07342234be5017a9189410514864d249679700b05cfa92ad6676e1
|
7
|
+
data.tar.gz: c9bde7fc8db24b18b9c47e8ac5c0b2d60a3d75fa549b442c2e64bd9f9807ab17794d5958dceb3f12c1711821a3c90150402e03748a0e2168499b0bac312c3bcf
|
data/.gitignore
CHANGED
data/Dockerfile
CHANGED
data/README.md
CHANGED
@@ -34,7 +34,7 @@ Oder installiere das Gem ohne ein Gemfile mit:
|
|
34
34
|
|
35
35
|
## Verwendung
|
36
36
|
|
37
|
-
Ein Beispielprojekt zur Verwendung der Bibliothek findet man im Verzeichnis `example` ([
|
37
|
+
Ein Beispielprojekt zur Verwendung der Bibliothek findet man im Verzeichnis `example` ([Beispielprojekt auf GitHub](https://github.com/CAU-Kiel-Tech-Inf/socha_ruby_client/tree/master/example)).
|
38
38
|
|
39
39
|
Du kannst den Beispielclient mittels
|
40
40
|
|
data/RELEASES.md
CHANGED
data/bin/console
CHANGED
File without changes
|
data/bin/setup
CHANGED
File without changes
|
data/develop.sh
CHANGED
File without changes
|
data/example/main.rb
CHANGED
File without changes
|
data/example/start.bat
CHANGED
File without changes
|
data/generate-authors.sh
CHANGED
File without changes
|
@@ -5,31 +5,16 @@ require_relative './util/constants'
|
|
5
5
|
require_relative 'game_state'
|
6
6
|
require_relative 'field'
|
7
7
|
|
8
|
-
# Ein Spielbrett fuer
|
8
|
+
# Ein Spielbrett fuer Ostseeschach
|
9
9
|
class Board
|
10
10
|
include Constants
|
11
|
+
|
11
12
|
# @!attribute [r] fields
|
12
13
|
# @note Besser über die {#field} Methode auf Felder zugreifen.
|
13
14
|
# @return [Array<Array<Field>>] Ein Feld wird an der Position entsprechend
|
14
15
|
# seiner x und y Coordinates im Array gespeichert.
|
15
16
|
attr_reader :fields
|
16
17
|
|
17
|
-
# @!attribute [r] deployed_blue_pieces
|
18
|
-
# @return [Array<PieceShape>] Die blauen, gesetzten Spielsteine
|
19
|
-
attr_accessor :deployed_blue_pieces
|
20
|
-
|
21
|
-
# @!attribute [r] deployed_yellow_pieces
|
22
|
-
# @return [Array<PieceShape>] Die gelben, gesetzten Spielsteine
|
23
|
-
attr_accessor :deployed_yellow_pieces
|
24
|
-
|
25
|
-
# @!attribute [r] deployed_red_pieces
|
26
|
-
# @return [Array<PieceShape>] Die roten, gesetzten Spielsteine
|
27
|
-
attr_accessor :deployed_red_pieces
|
28
|
-
|
29
|
-
# @!attribute [r] deployed_green_pieces
|
30
|
-
# @return [Array<PieceShape>] Die grünen, gesetzten Spielsteine
|
31
|
-
attr_accessor :deployed_green_pieces
|
32
|
-
|
33
18
|
# Erstellt ein neues leeres Spielbrett.
|
34
19
|
def initialize(fields = [])
|
35
20
|
@fields = Board.empty_game_field
|
@@ -91,39 +76,19 @@ class Board
|
|
91
76
|
field(coordinates.x, coordinates.y)
|
92
77
|
end
|
93
78
|
|
94
|
-
|
79
|
+
def fields_of_color(color)
|
80
|
+
fields = []
|
95
81
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
Coordinates.new(BOARD_SIZE - 1, BOARD_SIZE - 1),
|
103
|
-
Coordinates.new(BOARD_SIZE - 1, 0)].select { |it| field_at(it).color == color })
|
104
|
-
copy = Array.new(fields)
|
105
|
-
|
106
|
-
copy.each do |field|
|
107
|
-
[Coordinates.new(1, 0),
|
108
|
-
Coordinates.new(1, -1),
|
109
|
-
Coordinates.new(0, -1),
|
110
|
-
Coordinates.new(-1, -1),
|
111
|
-
Coordinates.new(-1, 0),
|
112
|
-
Coordinates.new(-1, 1),
|
113
|
-
Coordinates.new(0, 1),
|
114
|
-
Coordinates.new(1, 1)].each do |neighbor|
|
115
|
-
new_field = field + neighbor
|
116
|
-
next unless Board.contains(new_field) && @fields[new_field.x][new_field.y].color == color
|
117
|
-
|
118
|
-
fields << new_field unless fields.include?(new_field)
|
82
|
+
(0...BOARD_SIZE).to_a.map do |x|
|
83
|
+
(0...BOARD_SIZE).to_a.map do |y|
|
84
|
+
f = field(x,y)
|
85
|
+
if (f.color == color)
|
86
|
+
fields << f
|
87
|
+
end
|
119
88
|
end
|
120
89
|
end
|
121
90
|
|
122
|
-
|
123
|
-
fields
|
124
|
-
else
|
125
|
-
fields_of_color(color, fields)
|
126
|
-
end
|
91
|
+
fields
|
127
92
|
end
|
128
93
|
|
129
94
|
# @param it [Coordinates] Die zu untersuchenden Koordinaten
|
@@ -132,21 +97,6 @@ class Board
|
|
132
97
|
it.x >= 0 && it.y >= 0 && it.x < BOARD_SIZE && it.y < BOARD_SIZE
|
133
98
|
end
|
134
99
|
|
135
|
-
# @param color [Color] Die Farbe der Steine
|
136
|
-
# @return [Array<PieceShape>] Eine Liste aller Steintypen, die die gegebene Farbe noch nicht gespielt hat
|
137
|
-
def deployed_pieces(color)
|
138
|
-
case color
|
139
|
-
when Color::RED
|
140
|
-
deployed_red_pieces
|
141
|
-
when Color::BLUE
|
142
|
-
deployed_blue_pieces
|
143
|
-
when Color::YELLOW
|
144
|
-
deployed_yellow_pieces
|
145
|
-
when Color::GREEN
|
146
|
-
deployed_green_pieces
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
100
|
# @return eine unabhaengige Kopie des Spielbretts
|
151
101
|
def clone
|
152
102
|
Marshal.load(Marshal.dump(self))
|
@@ -2,15 +2,25 @@
|
|
2
2
|
|
3
3
|
require 'typesafe_enum'
|
4
4
|
|
5
|
-
|
5
|
+
require_relative 'team'
|
6
|
+
|
7
|
+
# TODO 2022: Replace with bool?
|
8
|
+
# Die Spielsteinfarben. BLUE, und RED
|
6
9
|
class Color < TypesafeEnum::Base
|
7
10
|
new :BLUE, 'B'
|
8
|
-
new :YELLOW, 'Y'
|
9
11
|
new :RED, 'R'
|
10
|
-
new :GREEN, 'G'
|
11
12
|
|
12
13
|
# Gibt den color namen zurück
|
13
14
|
def to_s
|
14
15
|
self.key.to_s
|
15
16
|
end
|
17
|
+
|
18
|
+
# Gibt das zugehörige Team zurück
|
19
|
+
def to_t
|
20
|
+
if self.key == :RED
|
21
|
+
Team::ONE
|
22
|
+
else
|
23
|
+
Team::TWO
|
24
|
+
end
|
25
|
+
end
|
16
26
|
end
|
@@ -4,47 +4,66 @@
|
|
4
4
|
# Ein Feld des Spielfelds. Ein Spielfeld ist durch die Koordinaten eindeutig
|
5
5
|
# identifiziert.
|
6
6
|
class Field
|
7
|
-
# @!attribute [rw] color
|
8
|
-
# @return [Color] Farbe des überdeckenden Spielsteins, falls vorhanden, sonst
|
9
|
-
# nil
|
10
|
-
attr_accessor :color
|
11
7
|
# @!attribute [r] coordinates
|
12
8
|
# @return [Coordinates] die X-Y-Koordinaten des Feldes
|
13
9
|
attr_reader :coordinates
|
14
10
|
|
11
|
+
# @!attribute [rw] piece
|
12
|
+
# @return [Piece] das Piece auf diesem Feld, falls vorhanden, sonst nil
|
13
|
+
attr_accessor :piece
|
14
|
+
|
15
15
|
# Erstellt ein neues leeres Feld.
|
16
16
|
#
|
17
17
|
# @param x [Integer] X-Koordinate
|
18
18
|
# @param y [Integer] Y-Koordinate
|
19
19
|
# @param color [Color] Farbe des Spielsteins, der das Feld überdeckt, nil falls kein Spielstein es überdeckt
|
20
|
-
def initialize(x, y,
|
21
|
-
@
|
20
|
+
def initialize(x, y, piece = nil)
|
21
|
+
@piece = piece
|
22
22
|
@coordinates = Coordinates.new(x, y)
|
23
23
|
end
|
24
24
|
|
25
25
|
# Vergleicht zwei Felder. Felder sind gleich, wenn sie gleiche Koordinaten und
|
26
|
-
# gleichen
|
26
|
+
# den gleichen Spielstein haben.
|
27
27
|
# @return [Boolean] true bei Gleichheit, sonst false.
|
28
28
|
def ==(other)
|
29
|
-
coordinates == other.coordinates &&
|
30
|
-
color == other.color
|
29
|
+
!other.nil? && coordinates == other.coordinates && piece == other.piece
|
31
30
|
end
|
32
31
|
|
32
|
+
# @return [Integer] X-Koordinate des Felds
|
33
33
|
def x
|
34
34
|
coordinates.x
|
35
35
|
end
|
36
36
|
|
37
|
+
# @return [Integer] Y-Koordinate des Felds
|
37
38
|
def y
|
38
39
|
coordinates.y
|
39
40
|
end
|
40
41
|
|
42
|
+
# @return [Team] Team des Pieces auf dem Feld
|
43
|
+
def team
|
44
|
+
if piece.nil?
|
45
|
+
nil
|
46
|
+
else
|
47
|
+
piece.color.to_t
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# @return [PieceColor] Farbe des Pieces auf dem Feld
|
52
|
+
def color
|
53
|
+
if piece.nil?
|
54
|
+
nil
|
55
|
+
else
|
56
|
+
piece.color
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
41
60
|
# @return [Boolean] true, wenn das Feld nicht durch einen Spielstein überdeckt ist, sonst false
|
42
61
|
def empty?
|
43
|
-
|
62
|
+
piece.nil?
|
44
63
|
end
|
45
64
|
|
46
65
|
# @return [String] Textuelle Darstellung des Feldes.
|
47
66
|
def to_s
|
48
|
-
empty? ? '
|
67
|
+
empty? ? '__' : piece.to_ss
|
49
68
|
end
|
50
69
|
end
|
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
require_relative './util/constants'
|
4
4
|
require_relative 'invalid_move_exception'
|
5
|
-
require_relative '
|
5
|
+
require_relative 'move'
|
6
6
|
|
7
7
|
require 'set'
|
8
8
|
|
9
|
-
# Methoden, welche die Spielregeln von
|
9
|
+
# Methoden, welche die Spielregeln von Ostseeschach abbilden.
|
10
10
|
#
|
11
11
|
# Es gibt hier viele Helfermethoden, die von den beiden Hauptmethoden {GameRuleLogic#valid_move?}
|
12
12
|
# und {GameRuleLogic.possible_moves} benutzt werden.
|
@@ -19,250 +19,69 @@ class GameRuleLogic
|
|
19
19
|
|
20
20
|
# Gibt alle möglichen Züge für den Spieler zurück, der in der gamestate dran ist.
|
21
21
|
# Diese ist die wichtigste Methode dieser Klasse für Schüler.
|
22
|
-
#
|
23
22
|
# @param gamestate [GameState] Der zu untersuchende Spielstand.
|
23
|
+
#
|
24
|
+
# @return [Array<Move>] Die möglichen Moves
|
24
25
|
def self.possible_moves(gamestate)
|
25
|
-
|
26
|
+
moves = []
|
27
|
+
fields = gamestate.board.fields_of_color(gamestate.current_player.color)
|
26
28
|
|
27
|
-
|
29
|
+
fields.each do |f|
|
30
|
+
moves.push(*moves_for_piece(gamestate, f.piece))
|
31
|
+
end
|
28
32
|
|
29
|
-
|
33
|
+
moves.select { |m| valid_move?(gamestate, m) }.to_a
|
30
34
|
end
|
31
35
|
|
32
36
|
# Gibt einen zufälligen möglichen Zug zurück
|
33
37
|
# @param gamestate [GameState] Der zu untersuchende Spielstand.
|
38
|
+
#
|
39
|
+
# @return [Move] Ein möglicher Move
|
34
40
|
def self.possible_move(gamestate)
|
35
41
|
possible_moves(gamestate).sample
|
36
42
|
end
|
37
43
|
|
38
|
-
#
|
44
|
+
# Hilfsmethode um Legezüge für einen [Piece] zu berechnen.
|
39
45
|
# @param gamestate [GameState] Der zu untersuchende Spielstand.
|
40
|
-
|
41
|
-
if gamestate.is_first_move?
|
42
|
-
possible_start_moves(gamestate)
|
43
|
-
else
|
44
|
-
all_possible_setmoves(gamestate).flatten
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
# Gibt alle möglichen Legezüge in der ersten Runde zurück
|
49
|
-
# @param gamestate [GameState] Der zu untersuchende Spielstand.
|
50
|
-
def self.possible_start_moves(gamestate)
|
51
|
-
color = gamestate.current_color
|
52
|
-
shape = gamestate.start_piece
|
53
|
-
area1 = shape.dimension
|
54
|
-
area2 = Coordinates.new(area1.y, area1.x)
|
55
|
-
moves = Set[]
|
56
|
-
|
57
|
-
# Hard code corners for most efficiency (and because a proper algorithm would be pretty illegible here)
|
58
|
-
# Upper Left
|
59
|
-
moves.merge(moves_for_shape_on(color, shape, Coordinates.new(0, 0)))
|
60
|
-
|
61
|
-
# Upper Right
|
62
|
-
moves.merge(moves_for_shape_on(color, shape, Coordinates.new(BOARD_SIZE - area1.x, 0)))
|
63
|
-
moves.merge(moves_for_shape_on(color, shape, Coordinates.new(BOARD_SIZE - area2.x, 0)))
|
64
|
-
|
65
|
-
# Lower Left
|
66
|
-
moves.merge(moves_for_shape_on(color, shape, Coordinates.new(0, BOARD_SIZE - area1.y)))
|
67
|
-
moves.merge(moves_for_shape_on(color, shape, Coordinates.new(0, BOARD_SIZE - area2.y)))
|
68
|
-
|
69
|
-
# Lower Right
|
70
|
-
moves.merge(moves_for_shape_on(color, shape, Coordinates.new(BOARD_SIZE - area1.x, BOARD_SIZE - area1.y)))
|
71
|
-
moves.merge(moves_for_shape_on(color, shape, Coordinates.new(BOARD_SIZE - area2.x, BOARD_SIZE - area2.y)))
|
72
|
-
|
73
|
-
moves.select { |m| valid_set_move?(gamestate, m) }.to_a
|
74
|
-
end
|
75
|
-
|
76
|
-
# Hilfsmethode um Legezüge für eine [PieceShape] zu berechnen.
|
77
|
-
# @param color [Color] Die Farbe der Spielsteine der Züge
|
78
|
-
# @param shape [PieceShape] Die Form der Spielsteine der Züge
|
79
|
-
# @param position [Coordinates] Die Position der Spielsteine der Züge
|
80
|
-
def self.moves_for_shape_on(color, shape, position)
|
81
|
-
moves = Set[]
|
82
|
-
Rotation.each do |r|
|
83
|
-
[true, false].each do |f|
|
84
|
-
moves << SetMove.new(Piece.new(color, shape, r, f, position))
|
85
|
-
end
|
86
|
-
end
|
87
|
-
moves
|
88
|
-
end
|
89
|
-
|
90
|
-
# Gib eine Liste aller möglichen Legezüge zurück, auch wenn es die erste Runde ist.
|
91
|
-
# @param gamestate [GameState] Der zu untersuchende Spielstand.
|
92
|
-
def self.all_possible_setmoves(gamestate)
|
93
|
-
moves = []
|
94
|
-
fields = valid_fields(gamestate)
|
95
|
-
gamestate.undeployed_pieces(gamestate.current_color).each do |p|
|
96
|
-
(moves << possible_moves_for_shape(gamestate, p, fields)).flatten
|
97
|
-
end
|
98
|
-
moves
|
99
|
-
end
|
100
|
-
|
101
|
-
# Gibt eine Liste aller möglichen SetMoves für diese Form zurück.
|
102
|
-
# @param gamestate [GameState] Der zu untersuchende Spielstand.
|
103
|
-
# @param shape Die [PieceShape], die die Züge nutzen sollen
|
46
|
+
# @param piece [Piece] Der Typ des Spielsteines
|
104
47
|
#
|
105
|
-
# @return
|
106
|
-
def self.
|
107
|
-
color = gamestate.current_color
|
108
|
-
|
48
|
+
# @return [Array<Move>] Die möglichen Moves
|
49
|
+
def self.moves_for_piece(gamestate, piece)
|
109
50
|
moves = Set[]
|
110
|
-
|
111
|
-
|
112
|
-
piece = Piece.new(color, shape, t.r, t.f, Coordinates.new(0, 0))
|
113
|
-
piece.coords.each do |pos|
|
114
|
-
moves << SetMove.new(Piece.new(color, shape, t.r, t.f, Coordinates.new(field.x - pos.x, field.y - pos.y)))
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
moves.select { |m| valid_set_move?(gamestate, m) }.to_a
|
119
|
-
end
|
120
|
-
|
121
|
-
# Gibt eine Liste aller Felder zurück, an denen möglicherweise Züge gemacht werden kann.
|
122
|
-
# @param gamestate [GameState] Der zu untersuchende Spielstand.
|
123
|
-
def self.valid_fields(gamestate)
|
124
|
-
color = gamestate.current_color
|
125
|
-
board = gamestate.board
|
126
|
-
fields = Set[]
|
127
|
-
board.fields_of_color(color).each do |field|
|
128
|
-
[Coordinates.new(field.x - 1, field.y - 1),
|
129
|
-
Coordinates.new(field.x - 1, field.y + 1),
|
130
|
-
Coordinates.new(field.x + 1, field.y - 1),
|
131
|
-
Coordinates.new(field.x + 1, field.y + 1)].each do |corner|
|
132
|
-
next unless Board.contains(corner)
|
133
|
-
next unless board[corner].empty?
|
134
|
-
next if neighbor_of_color?(board, Field.new(corner.x, corner.y), color)
|
135
|
-
|
136
|
-
fields << corner
|
137
|
-
end
|
138
|
-
end
|
139
|
-
fields
|
140
|
-
end
|
141
|
-
|
142
|
-
# Überprüft, ob das gegebene Feld ein Nachbarfeld mit der Farbe [color] hat
|
143
|
-
# @param board [Board] Das aktuelle Board
|
144
|
-
# @param field [Field] Das zu überprüfende Feld
|
145
|
-
# @param color [Color] Nach der zu suchenden Farbe
|
146
|
-
def self.neighbor_of_color?(board, field, color)
|
147
|
-
[Coordinates.new(field.x - 1, field.y),
|
148
|
-
Coordinates.new(field.x, field.y - 1),
|
149
|
-
Coordinates.new(field.x + 1, field.y),
|
150
|
-
Coordinates.new(field.x, field.y + 1)].any? do |neighbor|
|
151
|
-
Board.contains(neighbor) && board[neighbor].color == color
|
51
|
+
piece.target_coords.each do |c|
|
52
|
+
moves << Move.new(piece.position, c)
|
152
53
|
end
|
54
|
+
moves.select { |m| valid_move?(gamestate, m) }.to_a
|
153
55
|
end
|
154
56
|
|
155
|
-
# # Return a list of all moves, impossible or not.
|
156
|
-
# # There's no real usage, except maybe for cases where no Move validation happens
|
157
|
-
# # if `Constants.VALIDATE_MOVE` is false, then this function should return the same
|
158
|
-
# # Set as `::getPossibleMoves`
|
159
|
-
# def self.get_all_set_moves()
|
160
|
-
# moves = []
|
161
|
-
# Color.each do |c|
|
162
|
-
# PieceShape.each do |s|
|
163
|
-
# Rotation.each do |r|
|
164
|
-
# [false, true].each do |f|
|
165
|
-
# (0..BOARD_SIZE-1).to_a.each do |x|
|
166
|
-
# (0..BOARD_SIZE-1).to_a.each do |y|
|
167
|
-
# moves << SetMove.new(Piece.new(c, s, r, f, Coordinates.new(x, y)))
|
168
|
-
# end
|
169
|
-
# end
|
170
|
-
# end
|
171
|
-
# end
|
172
|
-
# end
|
173
|
-
# end
|
174
|
-
# moves
|
175
|
-
# end
|
176
|
-
|
177
57
|
# --- Move Validation ------------------------------------------------------------
|
178
58
|
|
179
59
|
# Prüft, ob der gegebene [Move] zulässig ist.
|
180
60
|
# @param gamestate [GameState] Der zu untersuchende Spielstand.
|
181
|
-
# @param move
|
61
|
+
# @param move [Move] Der zu überprüfende Zug
|
182
62
|
#
|
183
63
|
# @return ob der Zug zulässig ist
|
184
64
|
def self.valid_move?(gamestate, move)
|
185
|
-
|
186
|
-
!gamestate.is_first_move?
|
187
|
-
else
|
188
|
-
valid_set_move?(gamestate, move)
|
189
|
-
end
|
190
|
-
end
|
65
|
+
return false unless gamestate.current_player.color == move.piece(gamestate).color
|
191
66
|
|
192
|
-
|
193
|
-
# @param gamestate [GameState] der aktuelle Spielstand
|
194
|
-
# @param move [SetMove] der zu überprüfende Zug
|
195
|
-
#
|
196
|
-
# @return ob der Zug zulässig ist
|
197
|
-
def self.valid_set_move?(gamestate, move)
|
198
|
-
return false if move.piece.color != gamestate.current_color
|
67
|
+
return false unless gamestate.board.in_bounds?(move.to)
|
199
68
|
|
200
|
-
if gamestate.
|
201
|
-
# on first turn, only the start piece is allowed
|
202
|
-
return false if move.piece.kind != gamestate.start_piece
|
203
|
-
# and it may only be placed in a corner
|
204
|
-
return false if move.piece.coords.none? { |it| corner?(it) }
|
205
|
-
else
|
206
|
-
# in all other turns, only unused pieces may be placed
|
207
|
-
return false unless gamestate.undeployed_pieces(move.piece.color).include?(move.piece.kind)
|
208
|
-
# and it needs to be connected to another piece of the same color
|
209
|
-
return false if move.piece.coords.none? { |it| corners_on_color?(gamestate.board, it, move.piece.color) }
|
210
|
-
end
|
69
|
+
return false if gamestate.board.field_at(move.to).color == move.piece(gamestate).color
|
211
70
|
|
212
|
-
|
213
|
-
move.piece.coords.each do |it|
|
214
|
-
# - on the board
|
215
|
-
return false unless gamestate.board.in_bounds?(it)
|
216
|
-
# - on a empty field
|
217
|
-
return false if obstructed?(gamestate.board, it)
|
218
|
-
# - not next to a field occupied by the same color
|
219
|
-
return false if borders_on_color?(gamestate.board, it, move.piece.color)
|
220
|
-
end
|
71
|
+
return false unless move.piece(gamestate).target_coords.include? move.to
|
221
72
|
|
222
|
-
|
223
|
-
end
|
224
|
-
|
225
|
-
# Überprüft, ob das gegebene Feld ein Nachbarfeld mit der Farbe [color] hat
|
226
|
-
# @param board [Board] Das aktuelle Spielbrett
|
227
|
-
# @param field [Field] Das zu überprüfende Feld
|
228
|
-
# @param color [Color] Nach der zu suchenden Farbe
|
229
|
-
def self.borders_on_color?(board, position, color)
|
230
|
-
[Coordinates.new(1, 0), Coordinates.new(0, 1), Coordinates.new(-1, 0), Coordinates.new(0, -1)].any? do |it|
|
231
|
-
if board.in_bounds?(position + it)
|
232
|
-
board[position + it].color == color
|
233
|
-
else
|
234
|
-
false
|
235
|
-
end
|
236
|
-
end
|
237
|
-
end
|
238
|
-
|
239
|
-
# Überprüft, ob das gegebene Feld ein diagonales Nachbarfeld mit der Farbe [color] hat
|
240
|
-
# @param board [Board] Das aktuelle Spielbrett
|
241
|
-
# @param position [Field] Das zu überprüfende Feld
|
242
|
-
# @param color [Color] Nach der zu suchenden Farbe
|
243
|
-
def self.corners_on_color?(board, position, color)
|
244
|
-
[Coordinates.new(1, 1), Coordinates.new(1, -1), Coordinates.new(-1, -1), Coordinates.new(-1, 1)].any? do |it|
|
245
|
-
board.in_bounds?(position + it) && board[position + it].color == color
|
246
|
-
end
|
247
|
-
end
|
73
|
+
# TODO 2022: Forgot checks?
|
248
74
|
|
249
|
-
|
250
|
-
# @param position [Coordinates] Die zu überprüfenden Koordinaten
|
251
|
-
def self.corner?(position)
|
252
|
-
corner = [
|
253
|
-
Coordinates.new(0, 0),
|
254
|
-
Coordinates.new(BOARD_SIZE - 1, 0),
|
255
|
-
Coordinates.new(0, BOARD_SIZE - 1),
|
256
|
-
Coordinates.new(BOARD_SIZE - 1, BOARD_SIZE - 1)
|
257
|
-
]
|
258
|
-
corner.include? position
|
75
|
+
true
|
259
76
|
end
|
260
77
|
|
261
|
-
# Überprüft, ob die gegebene [position]
|
78
|
+
# Überprüft, ob die gegebene [position] mit einem Spielstein belegt ist.
|
262
79
|
# @param board [Board] Das aktuelle Spielbrett
|
263
80
|
# @param position [Coordinates] Die zu überprüfenden Koordinaten
|
81
|
+
#
|
82
|
+
# @return [Boolean] Ob die position belegt wurde
|
264
83
|
def self.obstructed?(board, position)
|
265
|
-
!board
|
84
|
+
!board.field_at(position).empty?
|
266
85
|
end
|
267
86
|
|
268
87
|
# --- Perform Move ------------------------------------------------------------
|
@@ -270,65 +89,53 @@ class GameRuleLogic
|
|
270
89
|
# Führe den gegebenen [Move] im gebenenen [GameState] aus.
|
271
90
|
# @param gamestate [GameState] der aktuelle Spielstand
|
272
91
|
# @param move der auszuführende Zug
|
92
|
+
#
|
93
|
+
# @return [GameState] Der theoretische GameState
|
273
94
|
def self.perform_move(gamestate, move)
|
274
95
|
raise 'Invalid move!' unless valid_move?(gamestate, move)
|
275
96
|
|
276
|
-
|
277
|
-
|
97
|
+
from_field = gamestate.board.field_at(move.from)
|
98
|
+
to_field = gamestate.board.field_at(move.to)
|
278
99
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
end
|
100
|
+
# Update board pieces if one is stepped on
|
101
|
+
if not to_field.empty?
|
102
|
+
from_field.piece.height = from_field.piece.height + 1
|
283
103
|
|
284
|
-
#
|
285
|
-
if
|
286
|
-
gamestate.
|
287
|
-
|
104
|
+
# Check for high tower
|
105
|
+
if from_field.piece.height >= 3
|
106
|
+
gamestate.current_player.amber = gamestate.current_player.amber + 1
|
107
|
+
to_field.piece = nil
|
288
108
|
end
|
289
109
|
end
|
110
|
+
|
111
|
+
# Update board fields
|
112
|
+
to_field.piece = from_field.piece
|
113
|
+
from_field.piece = nil
|
114
|
+
|
115
|
+
# Update position value of the moved piece
|
116
|
+
if !to_field.empty? && !to_field.piece.nil?
|
117
|
+
to_field.piece.position = Coordinates.new(to_field.coordinates.x, to_field.coordinates.y)
|
118
|
+
end
|
119
|
+
|
290
120
|
gamestate.turn += 1
|
291
|
-
gamestate.round += 1
|
292
121
|
gamestate.last_move = move
|
293
122
|
end
|
294
123
|
|
295
124
|
# --- Other ------------------------------------------------------------
|
296
125
|
|
297
|
-
#
|
298
|
-
# @param
|
299
|
-
# @param monoLast ob der letzte gelegte Stein das Monomino war
|
126
|
+
# Prueft, ob ein Spieler im gegebenen GameState gewonnen hat.
|
127
|
+
# @param gamestate [GameState] Der zu untersuchende GameState.
|
300
128
|
#
|
301
|
-
# @return
|
302
|
-
def self.
|
303
|
-
|
304
|
-
|
305
|
-
# Return sum of all squares plus 15 bonus points
|
306
|
-
return SUM_MAX_SQUARES + 15 +
|
307
|
-
# If the Monomino was the last placed piece, add another 5 points
|
308
|
-
mono_last ? 5 : 0
|
129
|
+
# @return [Condition] nil, if the game is not won or a Condition indicating the winning player
|
130
|
+
def self.winning_condition(gamestate)
|
131
|
+
if gamestate.player_one.amber >= 2
|
132
|
+
Condition.new(gamestate.player_one, "Spieler 1 hat 2 Bernsteine erreicht")
|
309
133
|
end
|
310
|
-
# One point per block per piece placed
|
311
|
-
SUM_MAX_SQUARES - undeployed.map(&:size).sum
|
312
|
-
end
|
313
|
-
|
314
|
-
# Gibt einen zufälligen Pentomino zurück, welcher nicht `x` ist.
|
315
|
-
def self.get_random_pentomino
|
316
|
-
PieceShape.map(&:value).select { |it| it.size == 5 && it != PieceShape::PENTO_X }
|
317
|
-
end
|
318
134
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
return unless get_possible_moves(gamestate).empty?
|
323
|
-
|
324
|
-
gamestate.remove_active_color
|
325
|
-
remove_invalid_colors(gamestate)
|
326
|
-
end
|
135
|
+
if gamestate.player_two.amber >= 2
|
136
|
+
Condition.new(gamestate.player_two, "Spieler 2 hat 2 Bernsteine erreicht")
|
137
|
+
end
|
327
138
|
|
328
|
-
|
329
|
-
# @param gamestate [GameState] Der zu untersuchende GameState.
|
330
|
-
# @return [Condition] nil, if the game is not won or a Condition indicating the winning player
|
331
|
-
def self.winning_condition(_gamestate)
|
332
|
-
raise 'Not implemented yet!'
|
139
|
+
nil
|
333
140
|
end
|
334
141
|
end
|