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