software_challenge_client 21.0.1 → 22.0.2
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/Dockerfile +1 -1
- data/RELEASES.md +12 -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.rb +3 -6
- 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 +55 -255
- data/lib/software_challenge_client/game_state.rb +18 -73
- data/lib/software_challenge_client/move.rb +41 -0
- data/lib/software_challenge_client/piece.rb +53 -35
- 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 +49 -112
- 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/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: 5409d523f45de264ef1616783fc0e901ea63460f831df5b65a014cc5effba23c
|
4
|
+
data.tar.gz: 1e9d622dbb0008fff464ca1e39a02ae3bab89192f07674ebd0f63b09b45f0fab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 02e77e4cc105dbccc0c52df6c3d3f7486cf36015d49ed08d6132c69983724e1cdff0fef12c0267b348137c5693b209aff80123dd5a297d28e0e6227ead3a9bbc
|
7
|
+
data.tar.gz: 9ee8e0a060c2b214353ca95647dd40bc844043688354663a9ccdd959269f38a70c85fae6bc531ec0eec5a862522e01c876984bc4bf1b4920de8e67f80d311c93
|
data/Dockerfile
CHANGED
data/RELEASES.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
= 21.2.0
|
2
|
+
|
3
|
+
Adjustments for Backend version 21.2.0
|
4
|
+
|
5
|
+
= 21.1.0
|
6
|
+
|
7
|
+
Pieces are now mutable and `==` of pieces considers rotated shapes which result in the same covered board fields as equal.
|
8
|
+
|
9
|
+
= 21.0.2
|
10
|
+
|
11
|
+
Fixed problem which caused `last_move` of a `GameState` always be `nil` (thanks to wollw!).
|
12
|
+
|
1
13
|
= 21.0.1
|
2
14
|
|
3
15
|
Improved performance and defined Ruby version 2.5.5 as minimum requirement
|
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
|
@@ -4,7 +4,6 @@ module SoftwareChallengeClient
|
|
4
4
|
require 'software_challenge_client/client_interface.rb'
|
5
5
|
require 'software_challenge_client/color.rb'
|
6
6
|
require 'software_challenge_client/condition.rb'
|
7
|
-
require 'software_challenge_client/coordinate_set.rb'
|
8
7
|
require 'software_challenge_client/coordinates.rb'
|
9
8
|
require 'software_challenge_client/debug_hint.rb'
|
10
9
|
require 'software_challenge_client/field.rb'
|
@@ -13,16 +12,14 @@ module SoftwareChallengeClient
|
|
13
12
|
require 'software_challenge_client/has_hints.rb'
|
14
13
|
require 'software_challenge_client/invalid_move_exception.rb'
|
15
14
|
require 'software_challenge_client/logging.rb'
|
15
|
+
require 'software_challenge_client/move.rb'
|
16
16
|
require 'software_challenge_client/network.rb'
|
17
17
|
require 'software_challenge_client/piece.rb'
|
18
|
-
require 'software_challenge_client/
|
18
|
+
require 'software_challenge_client/piece_type.rb'
|
19
19
|
require 'software_challenge_client/player.rb'
|
20
|
-
require 'software_challenge_client/player_type.rb'
|
21
20
|
require 'software_challenge_client/protocol.rb'
|
22
|
-
require 'software_challenge_client/rotation.rb'
|
23
21
|
require 'software_challenge_client/runner.rb'
|
24
|
-
require 'software_challenge_client/
|
25
|
-
require 'software_challenge_client/skip_move.rb'
|
22
|
+
require 'software_challenge_client/team.rb'
|
26
23
|
require 'software_challenge_client/util/constants.rb'
|
27
24
|
require 'software_challenge_client/version.rb'
|
28
25
|
end
|
@@ -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,251 +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
|
-
#
|
39
|
-
# @param gamestate [GameState] Der zu untersuchende Spielstand.
|
40
|
-
def self.possible_setmoves(gamestate)
|
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.
|
44
|
+
# Hilfsmethode um Legezüge für einen [Piece] zu berechnen.
|
102
45
|
# @param gamestate [GameState] Der zu untersuchende Spielstand.
|
103
|
-
# @param
|
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
|
-
# Check whether the color's move is currently active
|
199
|
-
return false if move.piece.color != gamestate.current_color
|
67
|
+
return false unless gamestate.board.in_bounds?(move.to)
|
200
68
|
|
201
|
-
|
202
|
-
if gamestate.is_first_move?
|
203
|
-
return false if move.piece.kind != gamestate.start_piece
|
204
|
-
elsif !gamestate.undeployed_pieces(move.piece.color).include?(move.piece.kind)
|
205
|
-
return false
|
206
|
-
end
|
69
|
+
return false if gamestate.board.field_at(move.to).color == move.piece(gamestate).color
|
207
70
|
|
208
|
-
|
209
|
-
move.piece.coords.each do |it|
|
210
|
-
return false unless gamestate.board.in_bounds?(it)
|
211
|
-
return false if obstructed?(gamestate.board, it)
|
212
|
-
return false if borders_on_color?(gamestate.board, it, move.piece.color)
|
213
|
-
end
|
71
|
+
return false unless move.piece(gamestate).target_coords.include? move.to
|
214
72
|
|
215
|
-
|
216
|
-
# Check if it is placed correctly in a corner
|
217
|
-
return false if move.piece.coords.none? { |it| corner?(it) }
|
218
|
-
else
|
219
|
-
# Check if the piece is connected to at least one tile of same color by corner
|
220
|
-
return false if move.piece.coords.none? { |it| corners_on_color?(gamestate.board, it, move.piece.color) }
|
221
|
-
end
|
73
|
+
# TODO 2022: Forgot checks?
|
222
74
|
|
223
75
|
true
|
224
76
|
end
|
225
77
|
|
226
|
-
# Überprüft, ob
|
227
|
-
# @param board [Board] Das aktuelle Spielbrett
|
228
|
-
# @param field [Field] Das zu überprüfende Feld
|
229
|
-
# @param color [Color] Nach der zu suchenden Farbe
|
230
|
-
def self.borders_on_color?(board, position, color)
|
231
|
-
[Coordinates.new(1, 0), Coordinates.new(0, 1), Coordinates.new(-1, 0), Coordinates.new(0, -1)].any? do |it|
|
232
|
-
if board.in_bounds?(position + it)
|
233
|
-
board[position + it].color == color
|
234
|
-
else
|
235
|
-
false
|
236
|
-
end
|
237
|
-
end
|
238
|
-
end
|
239
|
-
|
240
|
-
# Überprüft, ob das gegebene Feld ein diagonales Nachbarfeld mit der Farbe [color] hat
|
241
|
-
# @param board [Board] Das aktuelle Spielbrett
|
242
|
-
# @param position [Field] Das zu überprüfende Feld
|
243
|
-
# @param color [Color] Nach der zu suchenden Farbe
|
244
|
-
def self.corners_on_color?(board, position, color)
|
245
|
-
[Coordinates.new(1, 1), Coordinates.new(1, -1), Coordinates.new(-1, -1), Coordinates.new(-1, 1)].any? do |it|
|
246
|
-
board.in_bounds?(position + it) && board[position + it].color == color
|
247
|
-
end
|
248
|
-
end
|
249
|
-
|
250
|
-
# Überprüft, ob die gegebene [position] an einer Ecke des Boards liegt.
|
251
|
-
# @param position [Coordinates] Die zu überprüfenden Koordinaten
|
252
|
-
def self.corner?(position)
|
253
|
-
corner = [
|
254
|
-
Coordinates.new(0, 0),
|
255
|
-
Coordinates.new(BOARD_SIZE - 1, 0),
|
256
|
-
Coordinates.new(0, BOARD_SIZE - 1),
|
257
|
-
Coordinates.new(BOARD_SIZE - 1, BOARD_SIZE - 1)
|
258
|
-
]
|
259
|
-
corner.include? position
|
260
|
-
end
|
261
|
-
|
262
|
-
# Überprüft, ob die gegebene [position] schon mit einer Farbe belegt wurde.
|
78
|
+
# Überprüft, ob die gegebene [position] mit einem Spielstein belegt ist.
|
263
79
|
# @param board [Board] Das aktuelle Spielbrett
|
264
80
|
# @param position [Coordinates] Die zu überprüfenden Koordinaten
|
81
|
+
#
|
82
|
+
# @return [Boolean] Ob die position belegt wurde
|
265
83
|
def self.obstructed?(board, position)
|
266
|
-
!board
|
84
|
+
!board.field_at(position).empty?
|
267
85
|
end
|
268
86
|
|
269
87
|
# --- Perform Move ------------------------------------------------------------
|
@@ -271,66 +89,48 @@ class GameRuleLogic
|
|
271
89
|
# Führe den gegebenen [Move] im gebenenen [GameState] aus.
|
272
90
|
# @param gamestate [GameState] der aktuelle Spielstand
|
273
91
|
# @param move der auszuführende Zug
|
92
|
+
#
|
93
|
+
# @return [GameState] Der theoretische GameState
|
274
94
|
def self.perform_move(gamestate, move)
|
275
95
|
raise 'Invalid move!' unless valid_move?(gamestate, move)
|
276
96
|
|
277
|
-
|
278
|
-
|
279
|
-
# gamestate.deployed_pieces(move.piece.color).add(move.piece)
|
97
|
+
from_field = gamestate.board.field_at(move.from)
|
98
|
+
to_field = gamestate.board.field_at(move.to)
|
280
99
|
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
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
|
285
103
|
|
286
|
-
#
|
287
|
-
if
|
288
|
-
gamestate.
|
289
|
-
|
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
|
290
108
|
end
|
291
109
|
end
|
110
|
+
|
111
|
+
# Update board fields
|
112
|
+
to_field.piece = from_field.piece
|
113
|
+
from_field.piece = nil
|
114
|
+
|
292
115
|
gamestate.turn += 1
|
293
|
-
gamestate.round += 1
|
294
116
|
gamestate.last_move = move
|
295
117
|
end
|
296
118
|
|
297
119
|
# --- Other ------------------------------------------------------------
|
298
120
|
|
299
|
-
#
|
300
|
-
# @param
|
301
|
-
# @param monoLast ob der letzte gelegte Stein das Monomino war
|
121
|
+
# Prueft, ob ein Spieler im gegebenen GameState gewonnen hat.
|
122
|
+
# @param gamestate [GameState] Der zu untersuchende GameState.
|
302
123
|
#
|
303
|
-
# @return
|
304
|
-
def self.
|
305
|
-
|
306
|
-
|
307
|
-
# Return sum of all squares plus 15 bonus points
|
308
|
-
return SUM_MAX_SQUARES + 15 +
|
309
|
-
# If the Monomino was the last placed piece, add another 5 points
|
310
|
-
mono_last ? 5 : 0
|
124
|
+
# @return [Condition] nil, if the game is not won or a Condition indicating the winning player
|
125
|
+
def self.winning_condition(gamestate)
|
126
|
+
if gamestate.player_one.amber >= 2
|
127
|
+
Condition.new(gamestate.player_one, "Spieler 1 hat 2 Bernsteine erreicht")
|
311
128
|
end
|
312
|
-
# One point per block per piece placed
|
313
|
-
SUM_MAX_SQUARES - undeployed.map(&:size).sum
|
314
|
-
end
|
315
|
-
|
316
|
-
# Gibt einen zufälligen Pentomino zurück, welcher nicht `x` ist.
|
317
|
-
def self.get_random_pentomino
|
318
|
-
PieceShape.map(&:value).select { |it| it.size == 5 && it != PieceShape::PENTO_X }
|
319
|
-
end
|
320
129
|
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
return unless get_possible_moves(gamestate).empty?
|
325
|
-
|
326
|
-
gamestate.remove_active_color
|
327
|
-
remove_invalid_colors(gamestate)
|
328
|
-
end
|
130
|
+
if gamestate.player_two.amber >= 2
|
131
|
+
Condition.new(gamestate.player_two, "Spieler 2 hat 2 Bernsteine erreicht")
|
132
|
+
end
|
329
133
|
|
330
|
-
|
331
|
-
# @param gamestate [GameState] Der zu untersuchende GameState.
|
332
|
-
# @return [Condition] nil, if the game is not won or a Condition indicating the winning player
|
333
|
-
def self.winning_condition(_gamestate)
|
334
|
-
raise 'Not implemented yet!'
|
134
|
+
nil
|
335
135
|
end
|
336
136
|
end
|