software_challenge_client 21.2.0 → 22.1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|