software_challenge_client 20.2.2 → 21.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/.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 -4
- 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 +99 -34
- 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 +4 -1
- 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 -53
- 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 +43 -14
- 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 +83 -75
- 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 -23
- data/lib/software_challenge_client/direction.rb +0 -55
- data/lib/software_challenge_client/drag_move.rb +0 -19
- 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,60 @@
|
|
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
|
24
|
+
|
25
|
+
attr_reader :coords
|
10
26
|
|
11
|
-
|
12
|
-
|
27
|
+
# Erstellt einen neuen leeren Spielstein.
|
28
|
+
def initialize(color, kind, rotation = Rotation::NONE, is_flipped = false, position = Coordinates.origin)
|
13
29
|
@color = color
|
14
|
-
|
30
|
+
@kind = kind
|
31
|
+
@rotation = rotation
|
32
|
+
@is_flipped = is_flipped
|
33
|
+
@position = position
|
15
34
|
|
16
|
-
|
17
|
-
type == other.type && color == other.color
|
35
|
+
@coords = coords_priv
|
18
36
|
end
|
19
37
|
|
20
|
-
def
|
21
|
-
color
|
38
|
+
def ==(other)
|
39
|
+
color == other.color &&
|
40
|
+
kind == other.kind &&
|
41
|
+
rotation == other.rotation &&
|
42
|
+
is_flipped == other.is_flipped &&
|
43
|
+
position == other.position
|
22
44
|
end
|
23
45
|
|
24
46
|
def to_s
|
25
|
-
color.
|
47
|
+
"#{color.key} #{kind.key} at #{position} rotation #{rotation.key}#{is_flipped ? ' (flipped)' : ''}"
|
26
48
|
end
|
27
49
|
|
28
50
|
def inspect
|
29
51
|
to_s
|
30
52
|
end
|
53
|
+
|
54
|
+
private
|
55
|
+
def coords_priv
|
56
|
+
kind.transform(@rotation, @is_flipped).transform do |it|
|
57
|
+
Coordinates.new(it.x + @position.x, it.y + @position.y)
|
58
|
+
end.coordinates
|
59
|
+
end
|
31
60
|
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
|