software_challenge_client 19.1.0 → 20.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.ruby-version +1 -1
- data/.stickler.yml +7 -0
- data/RELEASES.md +4 -0
- data/example/client.rb +12 -1
- data/lib/software_challenge_client.rb +6 -4
- data/lib/software_challenge_client/board.rb +59 -40
- data/lib/software_challenge_client/cube_coordinates.rb +23 -0
- data/lib/software_challenge_client/direction.rb +23 -21
- data/lib/software_challenge_client/drag_move.rb +19 -0
- data/lib/software_challenge_client/field.rb +58 -21
- data/lib/software_challenge_client/game_rule_logic.rb +355 -198
- data/lib/software_challenge_client/game_state.rb +44 -15
- data/lib/software_challenge_client/has_hints.rb +11 -0
- data/lib/software_challenge_client/invalid_move_exception.rb +2 -2
- data/lib/software_challenge_client/network.rb +0 -2
- data/lib/software_challenge_client/piece.rb +31 -0
- data/lib/software_challenge_client/piece_type.rb +18 -0
- data/lib/software_challenge_client/player_color.rb +6 -16
- data/lib/software_challenge_client/protocol.rb +86 -18
- data/lib/software_challenge_client/set_move.rb +15 -0
- data/lib/software_challenge_client/skip_move.rb +8 -0
- data/lib/software_challenge_client/util/constants.rb +4 -2
- data/lib/software_challenge_client/version.rb +1 -1
- metadata +11 -9
- data/lib/software_challenge_client/coordinates.rb +0 -17
- data/lib/software_challenge_client/field_type.rb +0 -30
- data/lib/software_challenge_client/line.rb +0 -126
- data/lib/software_challenge_client/move.rb +0 -84
@@ -4,9 +4,7 @@
|
|
4
4
|
require_relative './util/constants'
|
5
5
|
require_relative 'player'
|
6
6
|
require_relative 'board'
|
7
|
-
require_relative 'move'
|
8
7
|
require_relative 'condition'
|
9
|
-
require_relative 'field_type'
|
10
8
|
|
11
9
|
# Ein Spielzustand. Wird vom Server an die Computerspieler übermittelt und
|
12
10
|
# enthält alles, was der Computerspieler wissen muss, um einen Zug zu machen.
|
@@ -25,6 +23,15 @@ class GameState
|
|
25
23
|
# @return [PlayerColor] Die Farbe des Spielers, der den nächsten Zug machen
|
26
24
|
# darf, der also gerade an der Reihe ist.
|
27
25
|
attr_accessor :current_player_color
|
26
|
+
|
27
|
+
# @!attribute [r] undeployed_red_pieces
|
28
|
+
# @return [Player] Die nicht gesetzten Spielsteine des roten Spielers
|
29
|
+
attr_accessor :undeployed_red_pieces
|
30
|
+
|
31
|
+
# @!attribute [r] undeployed_blue_pieces
|
32
|
+
# @return [Player] Die nicht gesetzten Spielsteine des roten Spielers
|
33
|
+
attr_accessor :undeployed_blue_pieces
|
34
|
+
|
28
35
|
# @!attribute [r] red
|
29
36
|
# @return [Player] Der rote Spieler
|
30
37
|
attr_reader :red
|
@@ -47,13 +54,30 @@ class GameState
|
|
47
54
|
def field(x, y)
|
48
55
|
board.field(x, y)
|
49
56
|
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
|
50
73
|
|
51
|
-
# Erstellt einen neuen Spielzustand.
|
52
74
|
def initialize
|
53
75
|
@current_player_color = PlayerColor::RED
|
54
76
|
@start_player_color = PlayerColor::RED
|
55
77
|
@board = Board.new
|
56
78
|
@turn = 0
|
79
|
+
@undeployed_red_pieces = GameState.parse_pieces_string(Constants::STARTING_PIECES, PlayerColor::RED)
|
80
|
+
@undeployed_blue_pieces = GameState.parse_pieces_string(Constants::STARTING_PIECES, PlayerColor::BLUE)
|
57
81
|
end
|
58
82
|
|
59
83
|
# Fügt einen Spieler zum Spielzustand hinzu.
|
@@ -89,6 +113,19 @@ class GameState
|
|
89
113
|
turn / 2
|
90
114
|
end
|
91
115
|
|
116
|
+
def undeployed_pieces(color)
|
117
|
+
case color
|
118
|
+
when PlayerColor::RED
|
119
|
+
undeployed_red_pieces
|
120
|
+
when PlayerColor::BLUE
|
121
|
+
undeployed_blue_pieces
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def deployed_pieces(color)
|
126
|
+
board.deployed_pieces(color)
|
127
|
+
end
|
128
|
+
|
92
129
|
# Führt einen Zug auf dem Spielzustand aus. Das Spielbrett wird entsprechend
|
93
130
|
# modifiziert.
|
94
131
|
#
|
@@ -122,7 +159,8 @@ class GameState
|
|
122
159
|
# @return [Integer] Die Punkte des Spielers, entspricht der Anzahl der Fische
|
123
160
|
# im größten Schwarm des Spielers.
|
124
161
|
def points_for_player(player)
|
125
|
-
|
162
|
+
# TODO
|
163
|
+
-1
|
126
164
|
end
|
127
165
|
|
128
166
|
def ==(other)
|
@@ -139,7 +177,7 @@ class GameState
|
|
139
177
|
# Erzeugt eine Kopie des Spielzustandes. Änderungen an dieser Kopie
|
140
178
|
# beeinflussen den originalen Spielzustand nicht. Die Kopie kann also zum
|
141
179
|
# testen von Spielzügen genutzt werden.
|
142
|
-
def
|
180
|
+
def clone
|
143
181
|
Marshal.load(Marshal.dump(self))
|
144
182
|
end
|
145
183
|
|
@@ -150,16 +188,7 @@ class GameState
|
|
150
188
|
|
151
189
|
# @return [Array<Field>] Alle Felder mit Fischen des Spielers, der gerade an der Reihe ist.
|
152
190
|
def own_fields
|
153
|
-
board.
|
154
|
-
PlayerColor.field_type(current_player_color)
|
155
|
-
)
|
156
|
-
end
|
157
|
-
|
158
|
-
# @return [Array<Move>] Alle regelkonformen Züge, die der aktuelle Spieler gerade machen könnte.
|
159
|
-
def possible_moves
|
160
|
-
own_fields.map do |f|
|
161
|
-
GameRuleLogic.possible_moves(board, f, current_player_color)
|
162
|
-
end.flatten
|
191
|
+
board.fields_of_color(current_player_color)
|
163
192
|
end
|
164
193
|
|
165
194
|
end
|
@@ -1,14 +1,14 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
# Exception, die geworfen wird, wenn ein ungültiger Zug ausgeführt wird.
|
4
|
-
# @see
|
4
|
+
# @see GameRuleLogic#perform_move
|
5
5
|
class InvalidMoveException < StandardError
|
6
6
|
def initialize(msg, move)
|
7
7
|
@move = move
|
8
8
|
super(msg)
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
11
|
+
def to_s
|
12
12
|
"#{super}: #{@move}"
|
13
13
|
end
|
14
14
|
end
|
@@ -68,14 +68,12 @@ class Network
|
|
68
68
|
sock_msg = ''
|
69
69
|
|
70
70
|
line = ''
|
71
|
-
logger.debug 'reading'
|
72
71
|
@socket.each_char do |char|
|
73
72
|
line += char
|
74
73
|
sock_msg += char
|
75
74
|
line = '' if ['\n', ' '].include? char
|
76
75
|
break if ['</room>', '</protocol>'].include? line
|
77
76
|
end
|
78
|
-
logger.debug 'ended reading'
|
79
77
|
if sock_msg != ''
|
80
78
|
@receive_buffer.concat(sock_msg)
|
81
79
|
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class Piece
|
2
|
+
|
3
|
+
# @!attribute [r] type
|
4
|
+
# @return [PieceType]
|
5
|
+
attr_reader :type
|
6
|
+
|
7
|
+
# @!attribute [r] color
|
8
|
+
# @return [PlayerColor]
|
9
|
+
attr_reader :color
|
10
|
+
|
11
|
+
def initialize(color, type)
|
12
|
+
@type = type
|
13
|
+
@color = color
|
14
|
+
end
|
15
|
+
|
16
|
+
def ==(other)
|
17
|
+
type == other.type && color == other.color
|
18
|
+
end
|
19
|
+
|
20
|
+
def owner
|
21
|
+
color
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
color.value + type.value
|
26
|
+
end
|
27
|
+
|
28
|
+
def inspect
|
29
|
+
to_s
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'typesafe_enum'
|
4
|
+
# Der Typ eines Spielsteins. Es gibt folgende Typen:
|
5
|
+
# - BEE
|
6
|
+
# - BEETLE
|
7
|
+
# - GRASSHOPPER
|
8
|
+
# - SPIDER
|
9
|
+
# - ANT
|
10
|
+
#
|
11
|
+
# Zugriff z.B. mit PieceType::BEE
|
12
|
+
class PieceType < TypesafeEnum::Base
|
13
|
+
new :BEE, 'Q'
|
14
|
+
new :BEETLE, 'B'
|
15
|
+
new :GRASSHOPPER, 'G'
|
16
|
+
new :SPIDER, 'S'
|
17
|
+
new :ANT, 'A'
|
18
|
+
end
|
@@ -2,11 +2,10 @@
|
|
2
2
|
# player color constants
|
3
3
|
require 'typesafe_enum'
|
4
4
|
|
5
|
-
# Die Spielerfarben. RED
|
5
|
+
# Die Spielerfarben. RED oder BLUE
|
6
6
|
class PlayerColor < TypesafeEnum::Base
|
7
|
-
new :
|
8
|
-
new :
|
9
|
-
new :BLUE
|
7
|
+
new :RED, 'R'
|
8
|
+
new :BLUE, 'B'
|
10
9
|
|
11
10
|
# @param color [PlayerColor]
|
12
11
|
# @return [PlayerColor] Farbe des Gegenspielers
|
@@ -16,20 +15,11 @@ class PlayerColor < TypesafeEnum::Base
|
|
16
15
|
PlayerColor::BLUE
|
17
16
|
when PlayerColor::BLUE
|
18
17
|
PlayerColor::RED
|
19
|
-
when PlayerColor::NONE
|
20
|
-
PlayerColor::NONE
|
21
18
|
end
|
22
19
|
end
|
23
20
|
|
24
|
-
|
25
|
-
|
26
|
-
# @see FieldType#player_color
|
27
|
-
def self.field_type(color)
|
28
|
-
case color
|
29
|
-
when PlayerColor::RED
|
30
|
-
FieldType::RED
|
31
|
-
when PlayerColor::BLUE
|
32
|
-
FieldType::BLUE
|
33
|
-
end
|
21
|
+
def opponent
|
22
|
+
PlayerColor.opponent_color(self)
|
34
23
|
end
|
24
|
+
|
35
25
|
end
|
@@ -2,7 +2,9 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
require 'socket'
|
4
4
|
require_relative 'board'
|
5
|
-
require_relative '
|
5
|
+
require_relative 'set_move'
|
6
|
+
require_relative 'drag_move'
|
7
|
+
require_relative 'skip_move'
|
6
8
|
require_relative 'player'
|
7
9
|
require_relative 'network'
|
8
10
|
require_relative 'client_interface'
|
@@ -40,7 +42,12 @@ class Protocol
|
|
40
42
|
# @param text [String] the xml-string that will be parsed
|
41
43
|
def process_string(text)
|
42
44
|
logger.debug "Parse XML:\n#{text}\n----END XML"
|
43
|
-
|
45
|
+
begin
|
46
|
+
REXML::Document.parse_stream(text, self)
|
47
|
+
rescue REXML::ParseException => e
|
48
|
+
# to parse incomplete xml, ignore missing end tag exceptions
|
49
|
+
raise e unless e.message =~ /Missing end tag/
|
50
|
+
end
|
44
51
|
end
|
45
52
|
|
46
53
|
|
@@ -113,29 +120,68 @@ class Protocol
|
|
113
120
|
when 'board'
|
114
121
|
logger.debug 'new board'
|
115
122
|
@gamestate.board = Board.new
|
116
|
-
@context[:current_tile_index] = nil
|
117
|
-
@context[:current_tile_direction] = nil
|
118
123
|
when 'field'
|
119
|
-
type = FieldType.find_by_key(attrs['state'].to_sym)
|
120
124
|
x = attrs['x'].to_i
|
121
125
|
y = attrs['y'].to_i
|
122
|
-
|
123
|
-
|
126
|
+
obstructed = attrs['isObstructed'] == 'true'
|
127
|
+
field = Field.new(x, y, [], obstructed)
|
128
|
+
@gamestate.board.add_field(field)
|
129
|
+
@context[:piece_target] = :field
|
130
|
+
@context[:field] = field
|
131
|
+
when 'piece'
|
132
|
+
owner = PlayerColor.find_by_key(attrs['owner'].to_sym)
|
133
|
+
type = PieceType.find_by_key(attrs['type'].to_sym)
|
134
|
+
piece = Piece.new(owner, type)
|
135
|
+
case @context[:piece_target]
|
136
|
+
when :field
|
137
|
+
@context[:field].add_piece(piece)
|
138
|
+
when :undeployed_red_pieces
|
139
|
+
@gamestate.undeployed_red_pieces << piece
|
140
|
+
when :undeployed_blue_pieces
|
141
|
+
@gamestate.undeployed_blue_pieces << piece
|
142
|
+
when :last_move
|
143
|
+
@context[:last_move_piece] = piece
|
144
|
+
else
|
145
|
+
raise "unknown piece target #{@context[:piece_target]}"
|
146
|
+
end
|
147
|
+
when 'undeployedRedPieces'
|
148
|
+
@context[:piece_target] = :undeployed_red_pieces
|
149
|
+
@gamestate.undeployed_red_pieces = []
|
150
|
+
when 'undeployedBluePieces'
|
151
|
+
@context[:piece_target] = :undeployed_blue_pieces
|
152
|
+
@gamestate.undeployed_blue_pieces = []
|
124
153
|
when 'lastMove'
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
154
|
+
type = attrs['class']
|
155
|
+
if type == 'skipmove'
|
156
|
+
@gamestate.last_move = SkipMove.new
|
157
|
+
else
|
158
|
+
@context[:last_move_type] = type
|
159
|
+
@context[:piece_target] = :last_move
|
160
|
+
end
|
161
|
+
when 'start'
|
162
|
+
@context[:last_move_start] = CubeCoordinates.new(attrs['x'].to_i, attrs['y'].to_i, attrs['z'].to_i)
|
163
|
+
when 'destination'
|
164
|
+
destination = CubeCoordinates.new(attrs['x'].to_i, attrs['y'].to_i, attrs['z'].to_i)
|
165
|
+
case @context[:last_move_type]
|
166
|
+
when 'setmove'
|
167
|
+
@gamestate.last_move = SetMove.new(@context[:last_move_piece], destination)
|
168
|
+
when 'dragmove'
|
169
|
+
@gamestate.last_move = SetMove.new(@context[:last_move_start], destination)
|
170
|
+
end
|
130
171
|
when 'winner'
|
131
|
-
|
132
|
-
|
172
|
+
# TODO
|
173
|
+
#winning_player = parsePlayer(attrs)
|
174
|
+
#@gamestate.condition = Condition.new(winning_player, @gamestate.condition.reason)
|
133
175
|
when 'score'
|
176
|
+
# TODO
|
134
177
|
# there are two score tags in the result, but reason attribute should be equal on both
|
135
|
-
|
178
|
+
#@gamestate.condition = Condition.new(@gamestate.condition.winner, attrs['reason'])
|
136
179
|
when 'left'
|
137
180
|
logger.debug 'got left event, terminating'
|
138
181
|
@network.disconnect
|
182
|
+
when 'sc.protocol.responses.CloseConnection'
|
183
|
+
logger.debug 'got left close connection event, terminating'
|
184
|
+
@network.disconnect
|
139
185
|
end
|
140
186
|
end
|
141
187
|
|
@@ -180,9 +226,31 @@ class Protocol
|
|
180
226
|
# class interface to supply a method which returns the XML
|
181
227
|
# because XML-generation should be decoupled from internal data
|
182
228
|
# structures.
|
183
|
-
|
184
|
-
|
185
|
-
|
229
|
+
case move
|
230
|
+
when SetMove
|
231
|
+
builder.data(class: 'setmove') do |data|
|
232
|
+
data.piece(owner: move.piece.owner.key, type: move.piece.type.key)
|
233
|
+
d = move.destination
|
234
|
+
data.destination(x: d.x, y: d.y, z: d.z)
|
235
|
+
move.hints.each do |hint|
|
236
|
+
data.hint(content: hint.content)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
when DragMove
|
240
|
+
builder.data(class: 'dragmove') do |data|
|
241
|
+
s = move.start
|
242
|
+
data.start(x: s.x, y: s.y, z: s.z)
|
243
|
+
d = move.destination
|
244
|
+
data.destination(x: d.x, y: d.y, z: d.z)
|
245
|
+
move.hints.each do |hint|
|
246
|
+
data.hint(content: hint.content)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
when SkipMove
|
250
|
+
builder.data(class: 'skipmove') do |data|
|
251
|
+
move.hints.each do |hint|
|
252
|
+
data.hint(content: hint.content)
|
253
|
+
end
|
186
254
|
end
|
187
255
|
end
|
188
256
|
builder.target!
|
@@ -4,6 +4,8 @@
|
|
4
4
|
# Konstanten zum aktuellen Spiel.
|
5
5
|
module Constants
|
6
6
|
ROUND_LIMIT = 30 # Rundenbegrenzung. Nach Ende der angegebenen Runde endet auch das Spiel.
|
7
|
-
|
8
|
-
|
7
|
+
GAME_IDENTIFIER = 'swc_2020_hive' # Der Identifikator des Spiels. Für die Kommunikation mit dem Spielserver.
|
8
|
+
STARTING_PIECES = 'QSSSGGBBAAA'
|
9
|
+
BOARD_SIZE = 11
|
10
|
+
SHIFT = ((BOARD_SIZE - 1) / 2)
|
9
11
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: software_challenge_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 20.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- 'kwollw '
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2019-11-05 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: typesafe_enum
|
@@ -249,6 +249,7 @@ files:
|
|
249
249
|
- ".rspec"
|
250
250
|
- ".rubocop.yml"
|
251
251
|
- ".ruby-version"
|
252
|
+
- ".stickler.yml"
|
252
253
|
- AUTHORS
|
253
254
|
- CODE_OF_CONDUCT.md
|
254
255
|
- Dockerfile
|
@@ -268,23 +269,26 @@ files:
|
|
268
269
|
- lib/software_challenge_client/board.rb
|
269
270
|
- lib/software_challenge_client/client_interface.rb
|
270
271
|
- lib/software_challenge_client/condition.rb
|
271
|
-
- lib/software_challenge_client/
|
272
|
+
- lib/software_challenge_client/cube_coordinates.rb
|
272
273
|
- lib/software_challenge_client/debug_hint.rb
|
273
274
|
- lib/software_challenge_client/direction.rb
|
275
|
+
- lib/software_challenge_client/drag_move.rb
|
274
276
|
- lib/software_challenge_client/field.rb
|
275
|
-
- lib/software_challenge_client/field_type.rb
|
276
277
|
- lib/software_challenge_client/game_rule_logic.rb
|
277
278
|
- lib/software_challenge_client/game_state.rb
|
279
|
+
- lib/software_challenge_client/has_hints.rb
|
278
280
|
- lib/software_challenge_client/invalid_move_exception.rb
|
279
|
-
- lib/software_challenge_client/line.rb
|
280
281
|
- lib/software_challenge_client/line_direction.rb
|
281
282
|
- lib/software_challenge_client/logging.rb
|
282
|
-
- lib/software_challenge_client/move.rb
|
283
283
|
- lib/software_challenge_client/network.rb
|
284
|
+
- lib/software_challenge_client/piece.rb
|
285
|
+
- lib/software_challenge_client/piece_type.rb
|
284
286
|
- lib/software_challenge_client/player.rb
|
285
287
|
- lib/software_challenge_client/player_color.rb
|
286
288
|
- lib/software_challenge_client/protocol.rb
|
287
289
|
- lib/software_challenge_client/runner.rb
|
290
|
+
- lib/software_challenge_client/set_move.rb
|
291
|
+
- lib/software_challenge_client/skip_move.rb
|
288
292
|
- lib/software_challenge_client/util/constants.rb
|
289
293
|
- lib/software_challenge_client/version.rb
|
290
294
|
- release.sh
|
@@ -307,11 +311,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
307
311
|
- !ruby/object:Gem::Version
|
308
312
|
version: '0'
|
309
313
|
requirements: []
|
310
|
-
|
311
|
-
rubygems_version: 2.6.11
|
314
|
+
rubygems_version: 3.0.3
|
312
315
|
signing_key:
|
313
316
|
specification_version: 4
|
314
317
|
summary: This gem provides functions to build a client for the coding competition
|
315
318
|
Software-Challenge 2017.
|
316
319
|
test_files: []
|
317
|
-
has_rdoc:
|