software_challenge_client 20.2.3 → 21.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -0
- data/.ruby-version +1 -1
- data/.vscode/launch.json +41 -0
- data/.vscode/settings.json +10 -0
- data/Dockerfile +1 -1
- data/Gemfile +1 -0
- data/Guardfile +1 -0
- data/README.md +4 -3
- data/RELEASES.md +20 -0
- data/Rakefile +4 -3
- data/example/client.rb +6 -9
- data/example/main.rb +9 -9
- data/lib/software_challenge_client.rb +26 -23
- data/lib/software_challenge_client/board.rb +94 -37
- data/lib/software_challenge_client/client_interface.rb +1 -0
- data/lib/software_challenge_client/color.rb +16 -0
- data/lib/software_challenge_client/condition.rb +2 -0
- data/lib/software_challenge_client/coordinate_set.rb +92 -0
- data/lib/software_challenge_client/coordinates.rb +45 -0
- data/lib/software_challenge_client/debug_hint.rb +1 -0
- data/lib/software_challenge_client/field.rb +21 -56
- data/lib/software_challenge_client/game_rule_logic.rb +257 -332
- data/lib/software_challenge_client/game_state.rb +86 -68
- data/lib/software_challenge_client/has_hints.rb +1 -1
- data/lib/software_challenge_client/invalid_move_exception.rb +1 -0
- data/lib/software_challenge_client/logging.rb +1 -0
- data/lib/software_challenge_client/network.rb +1 -1
- data/lib/software_challenge_client/piece.rb +64 -13
- data/lib/software_challenge_client/piece_shape.rb +109 -0
- data/lib/software_challenge_client/player.rb +7 -6
- data/lib/software_challenge_client/player_type.rb +14 -0
- data/lib/software_challenge_client/protocol.rb +82 -74
- data/lib/software_challenge_client/rotation.rb +22 -0
- data/lib/software_challenge_client/runner.rb +2 -1
- data/lib/software_challenge_client/set_move.rb +13 -4
- data/lib/software_challenge_client/skip_move.rb +5 -0
- data/lib/software_challenge_client/util/constants.rb +3 -4
- data/lib/software_challenge_client/version.rb +2 -1
- data/lib/update_client_module.sh +15 -0
- data/software_challenge_client.gemspec +15 -13
- metadata +54 -36
- data/lib/software_challenge_client/cube_coordinates.rb +0 -25
- data/lib/software_challenge_client/direction.rb +0 -55
- data/lib/software_challenge_client/drag_move.rb +0 -22
- data/lib/software_challenge_client/line_direction.rb +0 -15
- data/lib/software_challenge_client/piece_type.rb +0 -18
- data/lib/software_challenge_client/player_color.rb +0 -25
@@ -1,4 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
1
|
# frozen_string_literal: true
|
3
2
|
|
4
3
|
require_relative './util/constants'
|
@@ -15,32 +14,49 @@ class GameState
|
|
15
14
|
# @!attribute [rw] turn
|
16
15
|
# @return [Integer] Aktuelle Zugnummer (von 0 beginnend)
|
17
16
|
attr_accessor :turn
|
18
|
-
# @!attribute [rw]
|
19
|
-
# @return [
|
20
|
-
|
21
|
-
|
22
|
-
# @!attribute [rw]
|
23
|
-
# @return [
|
24
|
-
|
25
|
-
|
17
|
+
# @!attribute [rw] round
|
18
|
+
# @return [Integer] Aktuelle Rundennummer (von 1 beginnend)
|
19
|
+
attr_accessor :round
|
20
|
+
|
21
|
+
# @!attribute [rw] startColor
|
22
|
+
# @return [Color] Die Farbe, die zuerst legen darf
|
23
|
+
attr_accessor :start_color
|
24
|
+
# @!attribute [rw] current_color_index
|
25
|
+
# @return [Color] Der jetzige Index in der Zug Reihenfolge der Farben.
|
26
|
+
attr_accessor :current_color_index
|
27
|
+
# @!attribute [rw] ordered_colors
|
28
|
+
# @return [Array<Color>] Ein Array aller Farben die ziehen können in
|
29
|
+
# der Reihenfolge in der sie drankommen
|
30
|
+
attr_accessor :ordered_colors
|
31
|
+
|
32
|
+
# @!attribute [r] undeployed_blue_pieces
|
33
|
+
# @return [Array<PieceShape>] Die blauen, nicht gesetzten Spielsteine
|
34
|
+
attr_accessor :undeployed_blue_pieces
|
35
|
+
|
36
|
+
# @!attribute [r] undeployed_yellow_pieces
|
37
|
+
# @return [Array<PieceShape>] Die gelben, nicht gesetzten Spielsteine
|
38
|
+
attr_accessor :undeployed_yellow_pieces
|
26
39
|
|
27
40
|
# @!attribute [r] undeployed_red_pieces
|
28
|
-
# @return [
|
41
|
+
# @return [Array<PieceShape>] Die roten, nicht gesetzten Spielsteine
|
29
42
|
attr_accessor :undeployed_red_pieces
|
30
43
|
|
31
|
-
# @!attribute [r]
|
32
|
-
# @return [
|
33
|
-
attr_accessor :
|
44
|
+
# @!attribute [r] undeployed_green_pieces
|
45
|
+
# @return [Array<PieceShape>] Die grünen, nicht gesetzten Spielsteine
|
46
|
+
attr_accessor :undeployed_green_pieces
|
34
47
|
|
35
|
-
# @!attribute [r]
|
36
|
-
# @return [Player] Der
|
37
|
-
attr_reader :
|
38
|
-
# @!attribute [r]
|
39
|
-
# @return [Player] Der
|
40
|
-
attr_reader :
|
48
|
+
# @!attribute [r] player_one
|
49
|
+
# @return [Player] Der erste Spieler
|
50
|
+
attr_reader :player_one
|
51
|
+
# @!attribute [r] player_two
|
52
|
+
# @return [Player] Der zweite Spieler
|
53
|
+
attr_reader :player_two
|
41
54
|
# @!attribute [rw] board
|
42
55
|
# @return [Board] Das aktuelle Spielbrett
|
43
56
|
attr_accessor :board
|
57
|
+
# @!attribute [rw] startPiece
|
58
|
+
# @return [PieceShape] Der Stein, der im ersten Zug von allen Farben gelegt werden muss
|
59
|
+
attr_accessor :start_piece
|
44
60
|
# @!attribute [rw] last_move
|
45
61
|
# @return [Move] Der zuletzt gemachte Zug (ist nil vor dem ersten Zug, also
|
46
62
|
# bei turn == 0)
|
@@ -54,78 +70,75 @@ class GameState
|
|
54
70
|
def field(x, y)
|
55
71
|
board.field(x, y)
|
56
72
|
end
|
57
|
-
def self.parse_pieces_string(string, color)
|
58
|
-
string.chars.map do |c|
|
59
|
-
case c
|
60
|
-
when 'Q'
|
61
|
-
Piece.new(color, PieceType::BEE)
|
62
|
-
when 'S'
|
63
|
-
Piece.new(color, PieceType::SPIDER)
|
64
|
-
when 'G'
|
65
|
-
Piece.new(color, PieceType::GRASSHOPPER)
|
66
|
-
when 'B'
|
67
|
-
Piece.new(color, PieceType::BEETLE)
|
68
|
-
when 'A'
|
69
|
-
Piece.new(color, PieceType::ANT)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
73
|
|
74
|
+
# Erstellt einen neuen leeren Spielstand.
|
74
75
|
def initialize
|
75
|
-
@
|
76
|
-
@
|
76
|
+
@current_color = Color::RED
|
77
|
+
@start_color = Color::RED
|
77
78
|
@board = Board.new
|
78
79
|
@turn = 0
|
79
|
-
@
|
80
|
-
@
|
80
|
+
@undeployed_blue_pieces = PieceShape.to_a
|
81
|
+
@undeployed_yellow_pieces = PieceShape.to_a
|
82
|
+
@undeployed_red_pieces = PieceShape.to_a
|
83
|
+
@undeployed_green_pieces = PieceShape.to_a
|
84
|
+
@start_piece = GameRuleLogic.get_random_pentomino
|
81
85
|
end
|
82
86
|
|
83
87
|
# Fügt einen Spieler zum Spielzustand hinzu.
|
84
88
|
#
|
85
89
|
# @param player [Player] Der hinzuzufügende Spieler.
|
86
90
|
def add_player(player)
|
87
|
-
if player.
|
88
|
-
@
|
89
|
-
elsif player.
|
90
|
-
@
|
91
|
+
if player.type == PlayerType::ONE
|
92
|
+
@player_one = player
|
93
|
+
elsif player.type == PlayerType::TWO
|
94
|
+
@player_two = player
|
91
95
|
end
|
92
96
|
end
|
93
97
|
|
94
98
|
# @return [Player] Spieler, der gerade an der Reihe ist.
|
95
99
|
def current_player
|
96
|
-
|
97
|
-
return blue if current_player_color == PlayerColor::BLUE
|
100
|
+
turn % 2 == 0 ? player_one : player_two
|
98
101
|
end
|
99
102
|
|
100
103
|
# @return [Player] Spieler, der gerade nicht an der Reihe ist.
|
101
104
|
def other_player
|
102
|
-
|
103
|
-
return red if current_player_color == PlayerColor::BLUE
|
105
|
+
turn % 2 == 0 ? player_two : player_one
|
104
106
|
end
|
105
107
|
|
106
|
-
# @return [
|
107
|
-
def
|
108
|
-
|
108
|
+
# @return [PlayerType] Typ des Spielers, der gerade nicht an der Reihe ist.
|
109
|
+
def other_player_type
|
110
|
+
other_player.type
|
109
111
|
end
|
110
112
|
|
111
|
-
# @return [
|
112
|
-
def
|
113
|
-
|
113
|
+
# @return [Color] Farbe, der gerade an der Reihe ist.
|
114
|
+
def current_color
|
115
|
+
ordered_colors[current_color_index]
|
114
116
|
end
|
115
117
|
|
118
|
+
# @return [Array<PieceShape>] Array aller Shapes, der gegebenen Farbe, die noch nicht gelegt wurden
|
116
119
|
def undeployed_pieces(color)
|
117
120
|
case color
|
118
|
-
when
|
121
|
+
when Color::RED
|
119
122
|
undeployed_red_pieces
|
120
|
-
when
|
123
|
+
when Color::BLUE
|
121
124
|
undeployed_blue_pieces
|
125
|
+
when Color::YELLOW
|
126
|
+
undeployed_yellow_pieces
|
127
|
+
when Color::GREEN
|
128
|
+
undeployed_green_pieces
|
122
129
|
end
|
123
130
|
end
|
124
131
|
|
132
|
+
# @return [Array<PieceShape>] Array aller Shapes, der gegebenen Farbe, die schon gelegt wurden
|
125
133
|
def deployed_pieces(color)
|
126
134
|
board.deployed_pieces(color)
|
127
135
|
end
|
128
136
|
|
137
|
+
# @return [Bool] Ob diese gamestate in der ersten Runde ist
|
138
|
+
def is_first_move?
|
139
|
+
round == 1
|
140
|
+
end
|
141
|
+
|
129
142
|
# Führt einen Zug auf dem Spielzustand aus. Das Spielbrett wird entsprechend
|
130
143
|
# modifiziert.
|
131
144
|
#
|
@@ -140,6 +153,11 @@ class GameState
|
|
140
153
|
!condition.nil?
|
141
154
|
end
|
142
155
|
|
156
|
+
# Entfernt die jetzige Farbe aus der Farbrotation
|
157
|
+
def remove_active_color
|
158
|
+
ordered_colors.delete current_color
|
159
|
+
end
|
160
|
+
|
143
161
|
# @return [Player] Der Spieler, der das Spiel gewonnen hat, falls dies schon
|
144
162
|
# entschieden ist. Sonst false.
|
145
163
|
def winner
|
@@ -156,22 +174,23 @@ class GameState
|
|
156
174
|
# Rundenlimits beendet wird, hat der Spieler mit den meisten Punkten gewonnen.
|
157
175
|
#
|
158
176
|
# @param player [Player] Der Spieler, dessen Punkte berechnet werden sollen.
|
159
|
-
# @return [Integer] Die Punkte des Spielers
|
160
|
-
|
161
|
-
def points_for_player(player)
|
177
|
+
# @return [Integer] Die Punkte des Spielers
|
178
|
+
def points_for_player(_player)
|
162
179
|
# TODO
|
163
180
|
-1
|
164
181
|
end
|
165
182
|
|
166
183
|
def ==(other)
|
167
184
|
turn == other.turn &&
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
185
|
+
start_color == other.start_color &&
|
186
|
+
current_color == other.current_color &&
|
187
|
+
blue == other.blue &&
|
188
|
+
yellow == other.yellow &&
|
189
|
+
red == other.red &&
|
190
|
+
green == other.green &&
|
191
|
+
board == other.board &&
|
192
|
+
lastMove == other.lastMove &&
|
193
|
+
condition == other.condition
|
175
194
|
end
|
176
195
|
|
177
196
|
# Erzeugt eine Kopie des Spielzustandes. Änderungen an dieser Kopie
|
@@ -186,9 +205,8 @@ class GameState
|
|
186
205
|
@current_player_color = other_player_color
|
187
206
|
end
|
188
207
|
|
189
|
-
# @return [Array<Field>] Alle Felder mit
|
208
|
+
# @return [Array<Field>] Alle Felder mit Blöcken des Spielers, der gerade an der Reihe ist.
|
190
209
|
def own_fields
|
191
|
-
board.fields_of_color(
|
210
|
+
board.fields_of_color(current_color)
|
192
211
|
end
|
193
|
-
|
194
212
|
end
|
@@ -1,31 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Ein Spielstein mit Ausrichtung, Koordinaten und Farbe
|
1
4
|
class Piece
|
5
|
+
# @!attribute [r] Farbe
|
6
|
+
# @return [Color]
|
7
|
+
attr_reader :color
|
2
8
|
|
3
|
-
# @!attribute [r]
|
4
|
-
# @return [
|
5
|
-
attr_reader :
|
9
|
+
# @!attribute [r] Form
|
10
|
+
# @return [PieceShape]
|
11
|
+
attr_reader :kind
|
6
12
|
|
7
|
-
# @!attribute [r]
|
8
|
-
# @return [
|
9
|
-
attr_reader :
|
13
|
+
# @!attribute [r] Drehung
|
14
|
+
# @return [Rotation]
|
15
|
+
attr_reader :rotation
|
16
|
+
|
17
|
+
# @!attribute [r] Ob der Stein an der Y-Achse gespiegelt ist
|
18
|
+
# @return [Boolean]
|
19
|
+
attr_reader :is_flipped
|
20
|
+
|
21
|
+
# @!attribute [r] Koordinaten
|
22
|
+
# @return [Coordinates]
|
23
|
+
attr_reader :position
|
10
24
|
|
11
|
-
|
12
|
-
|
25
|
+
attr_reader :coords
|
26
|
+
|
27
|
+
# Erstellt einen neuen leeren Spielstein.
|
28
|
+
def initialize(color, kind, rotation = Rotation::NONE, is_flipped = false, position = Coordinates.origin)
|
13
29
|
@color = color
|
30
|
+
@kind = kind
|
31
|
+
@rotation = rotation
|
32
|
+
@is_flipped = is_flipped
|
33
|
+
@position = position
|
34
|
+
|
35
|
+
@coords = coords_priv
|
14
36
|
end
|
15
37
|
|
16
|
-
|
17
|
-
|
38
|
+
# Dreht den Stein
|
39
|
+
def rotate!(rotation)
|
40
|
+
@rotation = @rotation.rotate(rotation)
|
41
|
+
@coords = coords_priv
|
42
|
+
end
|
43
|
+
|
44
|
+
# Flipped den Stein
|
45
|
+
def flip!(f = true)
|
46
|
+
@is_flipped = @is_flipped ^ f
|
47
|
+
@coords = coords_priv
|
48
|
+
end
|
49
|
+
|
50
|
+
# Setzt den Stein auf eine Position
|
51
|
+
def locate!(position)
|
52
|
+
@position = position
|
53
|
+
@coords = coords_priv
|
18
54
|
end
|
19
55
|
|
20
|
-
|
21
|
-
|
56
|
+
# Verschiebt den Stein
|
57
|
+
def move!(shift)
|
58
|
+
@position = position + shift
|
59
|
+
@coords = coords_priv
|
60
|
+
end
|
61
|
+
|
62
|
+
def ==(other)
|
63
|
+
color == other.color &&
|
64
|
+
coords == other.coords
|
22
65
|
end
|
23
66
|
|
24
67
|
def to_s
|
25
|
-
color.
|
68
|
+
"#{color.key} #{kind.key} at #{position} rotation #{rotation.key}#{is_flipped ? ' (flipped)' : ''}"
|
26
69
|
end
|
27
70
|
|
28
71
|
def inspect
|
29
72
|
to_s
|
30
73
|
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def coords_priv
|
78
|
+
kind.transform(@rotation, @is_flipped).transform do |it|
|
79
|
+
Coordinates.new(it.x + @position.x, it.y + @position.y)
|
80
|
+
end.coordinates
|
81
|
+
end
|
31
82
|
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'typesafe_enum'
|
4
|
+
|
5
|
+
require_relative 'coordinates'
|
6
|
+
require_relative 'coordinate_set'
|
7
|
+
|
8
|
+
# Die Form eines Spielsteins. Es gibt folgende Formen:
|
9
|
+
#
|
10
|
+
# MONO
|
11
|
+
# DOMINO
|
12
|
+
# TRIO_L
|
13
|
+
# TRIO_I
|
14
|
+
# TETRO_O
|
15
|
+
# TETRO_T
|
16
|
+
# TETRO_I
|
17
|
+
# TETRO_L
|
18
|
+
# TETRO_Z
|
19
|
+
# PENTO_L
|
20
|
+
# PENTO_T
|
21
|
+
# PENTO_V
|
22
|
+
# PENTO_S
|
23
|
+
# PENTO_Z
|
24
|
+
# PENTO_I
|
25
|
+
# PENTO_P
|
26
|
+
# PENTO_W
|
27
|
+
# PENTO_U
|
28
|
+
# PENTO_R
|
29
|
+
# PENTO_X
|
30
|
+
# PENTO_Y
|
31
|
+
#
|
32
|
+
# Zugriff z.B. mit PieceShape::PENTO_S
|
33
|
+
class PieceShape < TypesafeEnum::Base
|
34
|
+
def self.c(x, y)
|
35
|
+
Coordinates.new(x, y)
|
36
|
+
end
|
37
|
+
new :MONO, [c(0, 0)]
|
38
|
+
new :DOMINO, [c(0, 0), c(1, 0)]
|
39
|
+
new :TRIO_L, [c(0, 0), c(0, 1), c(1, 1)]
|
40
|
+
new :TRIO_I, [c(0, 0), c(0, 1), c(0, 2)]
|
41
|
+
new :TETRO_O, [c(0, 0), c(1, 0), c(0, 1), c(1, 1)]
|
42
|
+
new :TETRO_T, [c(0, 0), c(1, 0), c(2, 0), c(1, 1)]
|
43
|
+
new :TETRO_I, [c(0, 0), c(0, 1), c(0, 2), c(0, 3)]
|
44
|
+
new :TETRO_L, [c(0, 0), c(0, 1), c(0, 2), c(1, 2)]
|
45
|
+
new :TETRO_Z, [c(0, 0), c(1, 0), c(1, 1), c(2, 1)]
|
46
|
+
new :PENTO_L, [c(0, 0), c(0, 1), c(0, 2), c(0, 3), c(1, 3)]
|
47
|
+
new :PENTO_T, [c(0, 0), c(1, 0), c(2, 0), c(1, 1), c(1, 2)]
|
48
|
+
new :PENTO_V, [c(0, 0), c(0, 1), c(0, 2), c(1, 2), c(2, 2)]
|
49
|
+
new :PENTO_S, [c(1, 0), c(2, 0), c(3, 0), c(0, 1), c(1, 1)]
|
50
|
+
new :PENTO_Z, [c(0, 0), c(1, 0), c(1, 1), c(1, 2), c(2, 2)]
|
51
|
+
new :PENTO_I, [c(0, 0), c(0, 1), c(0, 2), c(0, 3), c(0, 4)]
|
52
|
+
new :PENTO_P, [c(0, 0), c(1, 0), c(0, 1), c(1, 1), c(0, 2)]
|
53
|
+
new :PENTO_W, [c(0, 0), c(0, 1), c(1, 1), c(1, 2), c(2, 2)]
|
54
|
+
new :PENTO_U, [c(0, 0), c(0, 1), c(1, 1), c(2, 1), c(2, 0)]
|
55
|
+
new :PENTO_R, [c(0, 1), c(1, 1), c(1, 2), c(2, 1), c(2, 0)]
|
56
|
+
new :PENTO_X, [c(1, 0), c(0, 1), c(1, 1), c(2, 1), c(1, 2)]
|
57
|
+
new :PENTO_Y, [c(0, 1), c(1, 0), c(1, 1), c(1, 2), c(1, 3)]
|
58
|
+
|
59
|
+
@transformations
|
60
|
+
Transform = Struct.new(:r, :f, :coords)
|
61
|
+
|
62
|
+
# Anzahl Felder, die der Stein belegt
|
63
|
+
def size
|
64
|
+
value.size
|
65
|
+
end
|
66
|
+
|
67
|
+
# Die Felder, die der Stein belegt
|
68
|
+
def coordinates
|
69
|
+
CoordinateSet.new(value)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Eine Koordinate, die das kleinstmögliche Rechteck beschreibt, welches alle Felder umfasst.
|
73
|
+
def dimension
|
74
|
+
coordinates.area
|
75
|
+
end
|
76
|
+
|
77
|
+
# Erzeugt eine nach Rotation und Flip transformierte Form
|
78
|
+
def transform(rotation, flip)
|
79
|
+
coordinates.rotate(rotation).flip(flip)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Gibt alle Kombinationen aus Rotation und Flipping zurück, welche zu einzigartigen
|
83
|
+
# Koordinatenmengen dieser Form führen.
|
84
|
+
# @return [Array<Transform>] Transform Structs mit Rotation r, Boolean f
|
85
|
+
def unique_transforms()
|
86
|
+
if not defined? @transformations then
|
87
|
+
existing_transforms = []
|
88
|
+
|
89
|
+
Rotation.each do |r|
|
90
|
+
[true, false].each do |f|
|
91
|
+
new_transform = Transform.new(r, f, transform(r, f))
|
92
|
+
|
93
|
+
if existing_transforms.none? { |t| t.coords == new_transform.coords } then
|
94
|
+
existing_transforms << new_transform
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
@transformations = existing_transforms
|
100
|
+
end
|
101
|
+
|
102
|
+
@transformations
|
103
|
+
end
|
104
|
+
|
105
|
+
# Gibt den Form Namen zurück
|
106
|
+
def to_s
|
107
|
+
self.key.to_s
|
108
|
+
end
|
109
|
+
end
|