software_challenge_client 1.2.1 → 19.0.0
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 +3 -0
- data/README.md +64 -26
- data/example/client.rb +1 -71
- data/example/main.rb +1 -1
- data/lib/software_challenge_client.rb +4 -2
- data/lib/software_challenge_client/board.rb +66 -19
- data/lib/software_challenge_client/client_interface.rb +10 -5
- data/lib/software_challenge_client/condition.rb +2 -2
- data/lib/software_challenge_client/coordinates.rb +17 -0
- data/lib/software_challenge_client/debug_hint.rb +8 -4
- data/lib/software_challenge_client/direction.rb +53 -0
- data/lib/software_challenge_client/field.rb +26 -12
- data/lib/software_challenge_client/field_type.rb +25 -19
- data/lib/software_challenge_client/game_rule_logic.rb +230 -0
- data/lib/software_challenge_client/game_state.rb +45 -191
- data/lib/software_challenge_client/invalid_move_exception.rb +6 -8
- data/lib/software_challenge_client/line.rb +126 -0
- data/lib/software_challenge_client/line_direction.rb +15 -0
- data/lib/software_challenge_client/logging.rb +3 -2
- data/lib/software_challenge_client/move.rb +51 -38
- data/lib/software_challenge_client/network.rb +3 -1
- data/lib/software_challenge_client/player.rb +0 -39
- data/lib/software_challenge_client/player_color.rb +23 -13
- data/lib/software_challenge_client/protocol.rb +20 -83
- data/lib/software_challenge_client/runner.rb +2 -1
- data/lib/software_challenge_client/util/constants.rb +8 -5
- data/lib/software_challenge_client/version.rb +1 -1
- data/software_challenge_client.gemspec +2 -0
- metadata +24 -8
- data/lib/software_challenge_client/action.rb +0 -217
- data/lib/software_challenge_client/card_type.rb +0 -13
- data/lib/software_challenge_client/field_unavailable_exception.rb +0 -17
- data/lib/software_challenge_client/game_rules.rb +0 -376
@@ -1,16 +1,14 @@
|
|
1
|
-
# encoding:
|
1
|
+
# encoding: utf-8
|
2
2
|
|
3
|
-
# Exception
|
4
|
-
#
|
3
|
+
# Exception, die geworfen wird, wenn ein ungültiger Zug ausgeführt wird.
|
4
|
+
# @see Move#perform!
|
5
5
|
class InvalidMoveException < StandardError
|
6
|
-
def initialize(msg,
|
7
|
-
|
8
|
-
# depending where the rule violation was detected.
|
9
|
-
@move_or_action = move_or_action
|
6
|
+
def initialize(msg, move)
|
7
|
+
@move = move
|
10
8
|
super(msg)
|
11
9
|
end
|
12
10
|
|
13
11
|
def message
|
14
|
-
"#{super}: #{@
|
12
|
+
"#{super}: #{@move}"
|
15
13
|
end
|
16
14
|
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require_relative './util/constants'
|
5
|
+
require_relative 'direction'
|
6
|
+
require_relative 'coordinates'
|
7
|
+
require_relative 'line_direction'
|
8
|
+
|
9
|
+
# Ein Iterator, der alle Koordinatenpaare auf einer Linie des Spielbrettes enthält.
|
10
|
+
class Line
|
11
|
+
include Enumerable
|
12
|
+
include Constants
|
13
|
+
|
14
|
+
# Erzeugt einen neuen Iterator.
|
15
|
+
#
|
16
|
+
# @param start [Coordinates]
|
17
|
+
# @param direction [LineDirection]
|
18
|
+
def initialize(start, direction)
|
19
|
+
@direction = direction
|
20
|
+
@start = start
|
21
|
+
# we will iterate from left to right, so find the leftmost field on the line
|
22
|
+
# inside the field note that (0,0) is the lowest, leftmost point, x-axis
|
23
|
+
# goes to the right, y-axis goes up
|
24
|
+
case @direction
|
25
|
+
when LineDirection::HORIZONTAL
|
26
|
+
leftmost_x = 0
|
27
|
+
leftmost_y = @start.y
|
28
|
+
when LineDirection::VERTICAL
|
29
|
+
leftmost_x = @start.x
|
30
|
+
leftmost_y = SIZE - 1
|
31
|
+
when LineDirection::RISING_DIAGONAL
|
32
|
+
# for rising diagonals, we have to decrease x and y
|
33
|
+
shift = [@start.x, @start.y].min
|
34
|
+
leftmost_x = @start.x - shift
|
35
|
+
leftmost_y = @start.y - shift
|
36
|
+
when LineDirection::FALLING_DIAGONAL
|
37
|
+
# for falling diagonals, we have to decrease x and increase y
|
38
|
+
shift = [@start.x, (SIZE - 1) - @start.y].min
|
39
|
+
leftmost_x = @start.x - shift
|
40
|
+
leftmost_y = @start.y + shift
|
41
|
+
end
|
42
|
+
@xi = leftmost_x
|
43
|
+
@yi = leftmost_y
|
44
|
+
|
45
|
+
@members = []
|
46
|
+
while @xi >= 0 && @yi >= 0 && @xi < SIZE && @yi < SIZE
|
47
|
+
@members << Coordinates.new(@xi, @yi)
|
48
|
+
# horizontal lines and diagonals move right
|
49
|
+
if [LineDirection::HORIZONTAL,
|
50
|
+
LineDirection::RISING_DIAGONAL,
|
51
|
+
LineDirection::FALLING_DIAGONAL].include? @direction
|
52
|
+
@xi += 1
|
53
|
+
end
|
54
|
+
# vertical lines and falling diagonals move down
|
55
|
+
if [LineDirection::VERTICAL,
|
56
|
+
LineDirection::FALLING_DIAGONAL].include? @direction
|
57
|
+
@yi -= 1
|
58
|
+
elsif @direction == LineDirection::RISING_DIAGONAL
|
59
|
+
# rising diagonals move up
|
60
|
+
@yi += 1
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def each(&block)
|
66
|
+
@members.each(&block)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Begrenzt den Iterator auf Koordinatenpaare von Feldern innerhalb der gegebenen beiden Koordinatenpaare (exklusive).
|
70
|
+
# Kann als Filter verwendet werden.
|
71
|
+
#
|
72
|
+
# @example
|
73
|
+
# Line.new(
|
74
|
+
# Coordinates.new(2, 3), LineDirection::HORIZONTAL
|
75
|
+
# ).select do |c|
|
76
|
+
# Line.between(Coordinates.new(2, 3), Coordinates.new(5, 3), LineDirection::HORIZONTAL).call(c)
|
77
|
+
# end
|
78
|
+
def self.between(start, bound, direction)
|
79
|
+
lower_x = [start.x, bound.x].min
|
80
|
+
lower_y = [start.y, bound.y].min
|
81
|
+
higher_x = [start.x, bound.x].max
|
82
|
+
higher_y = [start.y, bound.y].max
|
83
|
+
proc do |f|
|
84
|
+
case direction
|
85
|
+
when LineDirection::HORIZONTAL
|
86
|
+
f.x > lower_x && f.x < higher_x
|
87
|
+
when LineDirection::VERTICAL
|
88
|
+
f.y > lower_y && f.y < higher_y
|
89
|
+
when LineDirection::RISING_DIAGONAL, LineDirection::FALLING_DIAGONAL
|
90
|
+
f.x > lower_x && f.x < higher_x && f.y > lower_y && f.y < higher_y
|
91
|
+
else
|
92
|
+
throw `unknown direction ${direction}`
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# @param line_direction [LineDirection] Ausrichtung der Linie
|
98
|
+
# @return [Array<Direction>] Die beiden Bewegungsrichtungen, die auf einer Linie mit der Ausrichtung möglich sind.
|
99
|
+
def self.directions_for_line_direction(line_direction)
|
100
|
+
case line_direction
|
101
|
+
when LineDirection::HORIZONTAL
|
102
|
+
[Direction::LEFT, Direction::RIGHT]
|
103
|
+
when LineDirection::VERTICAL
|
104
|
+
[Direction::UP, Direction::DOWN]
|
105
|
+
when LineDirection::RISING_DIAGONAL
|
106
|
+
[Direction::UP_RIGHT, Direction::DOWN_LEFT]
|
107
|
+
when LineDirection::FALLING_DIAGONAL
|
108
|
+
[Direction::UP_LEFT, Direction::DOWN_RIGHT]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# @param line_direction [Direction] Bewegungsrichtung
|
113
|
+
# @return [Array<Direction>] Die Ausrichtung einer Linie, die auf der Bewegungsrichtung liegt.
|
114
|
+
def self.line_direction_for_direction(direction)
|
115
|
+
case direction
|
116
|
+
when Direction::LEFT, Direction::RIGHT
|
117
|
+
LineDirection::HORIZONTAL
|
118
|
+
when Direction::UP, Direction::DOWN
|
119
|
+
LineDirection::VERTICAL
|
120
|
+
when Direction::UP_RIGHT, Direction::DOWN_LEFT
|
121
|
+
LineDirection::RISING_DIAGONAL
|
122
|
+
when Direction::UP_LEFT, Direction::DOWN_RIGHT
|
123
|
+
LineDirection::FALLING_DIAGONAL
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'typesafe_enum'
|
4
|
+
# Ausrichtung einer Linie auf dem Spielbrett. Mögliche Werte sind:
|
5
|
+
# - HORIZONTAL
|
6
|
+
# - VERTICAL
|
7
|
+
# - RISING_DIAGONAL
|
8
|
+
# - FALLING_DIAGONAL
|
9
|
+
|
10
|
+
class LineDirection < TypesafeEnum::Base
|
11
|
+
new :HORIZONTAL
|
12
|
+
new :VERTICAL
|
13
|
+
new :RISING_DIAGONAL
|
14
|
+
new :FALLING_DIAGONAL
|
15
|
+
end
|
@@ -1,9 +1,10 @@
|
|
1
|
+
# coding: utf-8
|
1
2
|
require 'logger'
|
2
3
|
|
3
|
-
#
|
4
|
+
# Dieses Modul kann inkludiert werden, um eine Logausgabe auf der Konsole verwenden zu können.
|
4
5
|
# See http://stackoverflow.com/a/6768164/390808
|
5
6
|
#
|
6
|
-
#
|
7
|
+
# Verwendung:
|
7
8
|
#
|
8
9
|
# class MyClass
|
9
10
|
# include Logging
|
@@ -1,70 +1,83 @@
|
|
1
|
-
# encoding:
|
1
|
+
# encoding: utf-8
|
2
2
|
require_relative 'debug_hint'
|
3
|
-
require_relative 'action'
|
4
3
|
|
5
|
-
#
|
6
|
-
# actions in a specific order.
|
4
|
+
# Ein Spielzug. Er ist definiert durch das Koordinatenpaar des Ausgangsfeldes (ein Fisch des Spielers, der den Zug machen will) und eine Bewegungsrichtung.
|
7
5
|
class Move
|
8
|
-
# @!attribute [r]
|
6
|
+
# @!attribute [r] x
|
7
|
+
# @return [Integer] X-Koordinate des Fisches, der bewegt werden soll. Die Spalte ganz links auf dem Spielbrett hat X-Koordinate 0, die ganz rechts 9.
|
8
|
+
attr_reader :x
|
9
|
+
|
10
|
+
# @!attribute [r] y
|
11
|
+
# @return [Integer] Y-Koordinate des Fisches, der bewegt werden soll. Die Zeile ganz unten auf dem Spielbrett hat Y-Koordinate 0, die ganz oben 9.
|
12
|
+
attr_reader :y
|
13
|
+
|
14
|
+
# @!attribute [r] direction
|
9
15
|
#
|
10
|
-
# @return [
|
11
|
-
|
12
|
-
attr_reader :actions
|
16
|
+
# @return [Direction] Die Richtung, in die bewegt werden soll.
|
17
|
+
attr_reader :direction
|
13
18
|
|
14
19
|
# @!attribute [r] hints
|
15
|
-
# @return [Array<DebugHint>]
|
20
|
+
# @return [Array<DebugHint>] Hinweise, die an den Zug angeheftet werden sollen. Siehe {DebugHint}.
|
16
21
|
attr_reader :hints
|
17
22
|
|
18
|
-
#
|
19
|
-
#
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
+
# Erstellt einen neuen Zug.
|
24
|
+
# @param x [Integer]
|
25
|
+
# @param y [Integer]
|
26
|
+
# @param direction [Direction]
|
27
|
+
def initialize(x, y, direction)
|
28
|
+
@x = x
|
29
|
+
@y = y
|
30
|
+
@direction = direction
|
31
|
+
@hints = []
|
23
32
|
end
|
24
33
|
|
25
|
-
#
|
26
|
-
# @param hint [DebugHint] the added hint
|
34
|
+
# @param hint [DebugHint]
|
27
35
|
def add_hint(hint)
|
28
36
|
@hints.push(hint)
|
29
37
|
end
|
30
38
|
|
31
39
|
def ==(other)
|
32
|
-
|
33
|
-
actions.zip(other.actions).map { |a, b| a == b }.all?
|
40
|
+
x == other.x && y == other.y && direction == other.direction
|
34
41
|
end
|
35
42
|
|
36
43
|
def to_s
|
37
|
-
"Move: #{
|
44
|
+
"Move: (#{x},#{y}) #{direction}"
|
38
45
|
end
|
39
46
|
|
40
|
-
|
41
|
-
|
47
|
+
# @return [Coordinates] Die Koordinaten des Ausgangsfeldes des Zuges als Koordinatenpaar.
|
48
|
+
def from_field
|
49
|
+
Coordinates.new(x, y)
|
42
50
|
end
|
43
51
|
|
44
|
-
|
45
|
-
|
52
|
+
# Überprüft, ob der Zug in dem gegebenen Spielzustand regelkonform ausgeführt werden kann.
|
53
|
+
# @param gamestate [GameState]
|
54
|
+
# @return [Boolean]
|
55
|
+
def valid?(gamestate)
|
56
|
+
GameRuleLogic.valid_move(self, gamestate.board)
|
46
57
|
end
|
47
58
|
|
59
|
+
# Führt den Zug in dem gegebenen Spielzustand aus. Sollte dabei gegen Spielregeln verstossen werden, wird eine InvalidMoveException geworfen.
|
60
|
+
# @param gamestate [GameState]
|
48
61
|
def perform!(gamestate)
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
62
|
+
if GameRuleLogic.valid_move(self, gamestate.board)
|
63
|
+
type = gamestate.board.field(x, y).type
|
64
|
+
gamestate.board.change_field(x, y, FieldType::EMPTY)
|
65
|
+
target = GameRuleLogic.move_target(self, gamestate.board)
|
66
|
+
gamestate.board.change_field(target.x, target.y, type)
|
67
|
+
else
|
68
|
+
raise InvalidMoveException.new('Invalid move', self)
|
54
69
|
end
|
55
|
-
raise InvalidMoveException.new(
|
56
|
-
'Es muss eine Karte gespielt werden.',
|
57
|
-
self) if gamestate.current_player.must_play_card
|
58
70
|
# change the state to the next turn
|
59
71
|
gamestate.last_move = self
|
60
72
|
gamestate.turn += 1
|
61
73
|
gamestate.switch_current_player
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
# Ermittelt die Koordinaten des Zielfeldes des Zuges mit einer gegebenen Zugweite.
|
78
|
+
# @param speed [Integer] Die Zugweite. Entspricht normalerweise der Anzahl der Fische auf der Bewegungslinie.
|
79
|
+
# @return [Coordinates] Koordinaten des Zielfeldes. Eventuell ausserhalb des Spielbrettes.
|
80
|
+
def target_field(speed)
|
81
|
+
direction.translate(from_field, speed)
|
69
82
|
end
|
70
83
|
end
|
@@ -7,10 +7,12 @@ require 'rexml/element'
|
|
7
7
|
require_relative 'protocol'
|
8
8
|
require_relative 'board'
|
9
9
|
require_relative 'client_interface'
|
10
|
+
require_relative 'util/constants'
|
10
11
|
|
11
12
|
# This class handles the socket connection to the server
|
12
13
|
class Network
|
13
14
|
include Logging
|
15
|
+
include Constants
|
14
16
|
|
15
17
|
# @!attribute [r] connected
|
16
18
|
# @return [Boolean] true, if the client is connected to a server
|
@@ -43,7 +45,7 @@ class Network
|
|
43
45
|
element.add_attribute('reservationCode', @reservation_id)
|
44
46
|
else
|
45
47
|
element = REXML::Element.new('join')
|
46
|
-
element.add_attribute('gameType',
|
48
|
+
element.add_attribute('gameType', GAME_IDENTIFIER)
|
47
49
|
end
|
48
50
|
document.add(element)
|
49
51
|
sendXML(document)
|
@@ -1,5 +1,4 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
-
require_relative 'card_type'
|
3
2
|
|
4
3
|
# Ein Spieler
|
5
4
|
class Player
|
@@ -11,53 +10,15 @@ class Player
|
|
11
10
|
# @return [PlayerColor] die Farbe des Spielers, Rot oder Blau
|
12
11
|
attr_reader :color
|
13
12
|
|
14
|
-
# @!attribute [rw] points
|
15
|
-
# @return [Integer] der aktuelle Punktestand des Spielers
|
16
|
-
attr_accessor :points
|
17
|
-
|
18
|
-
# @!attribute [rw] index
|
19
|
-
# @return [Integer] die aktuelle Position des Spielers auf dem Spielbrett,
|
20
|
-
# entspricht index des Feldes, von 0 bis 64
|
21
|
-
attr_accessor :index
|
22
|
-
|
23
|
-
# @!attribute [rw] carrots
|
24
|
-
# @return [Integer] die aktuelle Anzahl Karotten des Spielers
|
25
|
-
attr_accessor :carrots
|
26
|
-
|
27
|
-
# @!attribute [rw] salads
|
28
|
-
# @return [Integer] die aktuelle Anzahl Salate des Spielers
|
29
|
-
attr_accessor :salads
|
30
|
-
|
31
|
-
# @!attribute [rw] cards
|
32
|
-
# @return [Array[CardType]] die noch nicht gespielten Karten
|
33
|
-
attr_accessor :cards
|
34
|
-
|
35
|
-
# @!attribute [rw] last_non_skip_action
|
36
|
-
# @return [Action] letzte Aktion, die kein Skip war
|
37
|
-
attr_accessor :last_non_skip_action
|
38
|
-
|
39
|
-
# @!attribute [rw] must_play_card
|
40
|
-
# @return [Boolean] zeigt an, ob eine Karte gespielt werden muss, wird in Zugvalidierung verwendet.
|
41
|
-
attr_accessor :must_play_card
|
42
|
-
|
43
13
|
# Konstruktor
|
44
14
|
# @param color [PlayerColor] Farbe
|
45
15
|
# @param name [String] Name
|
46
16
|
def initialize(color, name)
|
47
17
|
@color = color
|
48
18
|
@name = name
|
49
|
-
@points = 0
|
50
|
-
@index = 0
|
51
|
-
@carrots = 68
|
52
|
-
@salads = 5
|
53
|
-
@cards = CardType.to_a
|
54
19
|
end
|
55
20
|
|
56
21
|
def ==(other)
|
57
22
|
color == other.color
|
58
23
|
end
|
59
|
-
|
60
|
-
def owns_card_of_type(card_type)
|
61
|
-
cards.include? card_type
|
62
|
-
end
|
63
24
|
end
|
@@ -1,25 +1,35 @@
|
|
1
|
-
# encoding:
|
1
|
+
# encoding: utf-8
|
2
2
|
# player color constants
|
3
3
|
require 'typesafe_enum'
|
4
|
-
class PlayerColor < TypesafeEnum::Base
|
5
4
|
|
5
|
+
# Die Spielerfarben. RED, BLUE oder NONE.
|
6
|
+
class PlayerColor < TypesafeEnum::Base
|
6
7
|
new :NONE
|
7
8
|
new :RED
|
8
9
|
new :BLUE
|
9
10
|
|
10
|
-
#
|
11
|
-
#
|
12
|
-
# @param color [PlayerColor] The player's color, whose opponent needs to be found
|
13
|
-
# @return [PlayerColor] the opponent's color
|
11
|
+
# @param color [PlayerColor]
|
12
|
+
# @return [PlayerColor] Farbe des Gegenspielers
|
14
13
|
def self.opponent_color(color)
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
14
|
+
case color
|
15
|
+
when PlayerColor::RED
|
16
|
+
PlayerColor::BLUE
|
17
|
+
when PlayerColor::BLUE
|
18
|
+
PlayerColor::RED
|
19
|
+
when PlayerColor::NONE
|
20
|
+
PlayerColor::NONE
|
20
21
|
end
|
21
|
-
|
22
|
-
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param color [PlayerColor] Die Spielerfarbe, zu dem der Feldtyp ermittelt werden soll.
|
25
|
+
# @return [FieldType] Der zur Spielerfarbe gehörende Feldtyp, also FieldType::RED für PlayerColor::RED und FieldType::BLUE für PlayerColor::BLUE. In allen anderen Fällen nil.
|
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
|
23
33
|
end
|
24
34
|
end
|
25
35
|
end
|
@@ -56,8 +56,6 @@ class Protocol
|
|
56
56
|
case name
|
57
57
|
when 'board'
|
58
58
|
logger.debug @gamestate.board.to_s
|
59
|
-
when 'type'
|
60
|
-
@context[:player].cards << CardType.find_by_key(@context[:last_text].to_sym)
|
61
59
|
end
|
62
60
|
end
|
63
61
|
|
@@ -92,8 +90,8 @@ class Protocol
|
|
92
90
|
logger.debug 'new gamestate'
|
93
91
|
@gamestate = GameState.new
|
94
92
|
@gamestate.turn = attrs['turn'].to_i
|
95
|
-
@gamestate.start_player_color = attrs['
|
96
|
-
@gamestate.current_player_color = attrs['
|
93
|
+
@gamestate.start_player_color = attrs['startPlayerColor'] == 'RED' ? PlayerColor::RED : PlayerColor::BLUE
|
94
|
+
@gamestate.current_player_color = attrs['currentPlayerColor'] == 'RED' ? PlayerColor::RED : PlayerColor::BLUE
|
97
95
|
logger.debug "Turn: #{@gamestate.turn}"
|
98
96
|
when 'red'
|
99
97
|
logger.debug 'new red player'
|
@@ -116,38 +114,18 @@ class Protocol
|
|
116
114
|
@gamestate.board = Board.new
|
117
115
|
@context[:current_tile_index] = nil
|
118
116
|
@context[:current_tile_direction] = nil
|
119
|
-
when '
|
120
|
-
type = FieldType.find_by_key(attrs['
|
121
|
-
|
117
|
+
when 'field'
|
118
|
+
type = FieldType.find_by_key(attrs['state'].to_sym)
|
119
|
+
x = attrs['x'].to_i
|
120
|
+
y = attrs['y'].to_i
|
122
121
|
raise "unexpected field type: #{attrs['type']}. Known types are #{FieldType.map { |t| t.key.to_s }}" if type.nil?
|
123
|
-
@gamestate.board.
|
122
|
+
@gamestate.board.add_field(Field.new(x, y, type))
|
124
123
|
when 'lastMove'
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
)
|
130
|
-
when 'card'
|
131
|
-
@gamestate.last_move.add_action_with_order(
|
132
|
-
Card.new(CardType.find_by_key(attrs['type'].to_sym), attrs['value'].to_i),
|
133
|
-
attrs['order'].to_i
|
134
|
-
)
|
135
|
-
when 'skip'
|
136
|
-
@gamestate.last_move.add_action_with_order(
|
137
|
-
Skip.new, attrs['order'].to_i
|
138
|
-
)
|
139
|
-
when 'eatSalad'
|
140
|
-
@gamestate.last_move.add_action_with_order(
|
141
|
-
EatSalad.new, attrs['order'].to_i
|
142
|
-
)
|
143
|
-
when 'fallBack'
|
144
|
-
@gamestate.last_move.add_action_with_order(
|
145
|
-
FallBack.new, attrs['order'].to_i
|
146
|
-
)
|
147
|
-
when 'exchangeCarrots'
|
148
|
-
@gamestate.last_move.add_action_with_order(
|
149
|
-
ExchangeCarrots.new(attrs['value'].to_i), attrs['order'].to_i
|
150
|
-
)
|
124
|
+
direction = Direction.find_by_key(attrs['direction'].to_sym)
|
125
|
+
x = attrs['x'].to_i
|
126
|
+
y = attrs['y'].to_i
|
127
|
+
raise "unexpected direction: #{attrs['direction']}. Known directions are #{Direction.map { |d| d.key.to_s }}" if direction.nil?
|
128
|
+
@gamestate.last_move = Move.new(x, y, direction)
|
151
129
|
when 'winner'
|
152
130
|
winning_player = parsePlayer(attrs)
|
153
131
|
@gamestate.condition = Condition.new(winning_player)
|
@@ -155,24 +133,6 @@ class Protocol
|
|
155
133
|
when 'left'
|
156
134
|
logger.debug 'got left event, terminating'
|
157
135
|
@network.disconnect
|
158
|
-
when 'lastNonSkipAction'
|
159
|
-
@context[:player].last_non_skip_action =
|
160
|
-
case attrs['class']
|
161
|
-
when 'advance'
|
162
|
-
Advance.new(attrs['distance'].to_i)
|
163
|
-
when 'card'
|
164
|
-
Card.new(CardType.find_by_key(attrs['type'].to_sym), attrs['value'].to_i)
|
165
|
-
when 'skip'
|
166
|
-
Skip.new
|
167
|
-
when 'eatSalad'
|
168
|
-
EatSalad.new
|
169
|
-
when 'fallBack'
|
170
|
-
FallBack.new
|
171
|
-
when 'exchangeCarrots'
|
172
|
-
ExchangeCarrots.new(attrs['value'].to_i)
|
173
|
-
else
|
174
|
-
raise "Unknown action type #{attrs['class']}"
|
175
|
-
end
|
176
136
|
end
|
177
137
|
end
|
178
138
|
|
@@ -181,16 +141,10 @@ class Protocol
|
|
181
141
|
# @param attributes [Hash] Attributes for the new Player.
|
182
142
|
# @return [Player] The created Player object.
|
183
143
|
def parsePlayer(attributes)
|
184
|
-
|
144
|
+
Player.new(
|
185
145
|
PlayerColor.find_by_key(attributes['color'].to_sym),
|
186
146
|
attributes['displayName']
|
187
147
|
)
|
188
|
-
player.points = attributes['points'].to_i
|
189
|
-
player.index = attributes['index'].to_i
|
190
|
-
player.carrots = attributes['carrots'].to_i
|
191
|
-
player.salads = attributes['salads'].to_i
|
192
|
-
player.cards = []
|
193
|
-
player
|
194
148
|
end
|
195
149
|
|
196
150
|
# send a xml document
|
@@ -219,32 +173,15 @@ class Protocol
|
|
219
173
|
# @param move [Move] The move to convert to XML.
|
220
174
|
def move_to_xml(move)
|
221
175
|
builder = Builder::XmlMarkup.new(indent: 2)
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
when :advance
|
230
|
-
{ distance: action.distance }
|
231
|
-
when :skip, :eat_salad, :fall_back
|
232
|
-
{}
|
233
|
-
when :card
|
234
|
-
{ type: action.card_type.key.to_s, value: action.value }
|
235
|
-
when :exchange_carrots
|
236
|
-
{ value: action.value }
|
237
|
-
else
|
238
|
-
raise "unknown action type: #{action.type.inspect}. "\
|
239
|
-
"Can't convert to XML!"
|
240
|
-
end
|
241
|
-
attribute[:order] = index
|
242
|
-
data.tag!(snake_case_to_lower_camel_case(action.type.to_s), attribute)
|
176
|
+
# Converting every the move here instead of requiring the Move
|
177
|
+
# class interface to supply a method which returns the XML
|
178
|
+
# because XML-generation should be decoupled from internal data
|
179
|
+
# structures.
|
180
|
+
builder.data(class: 'move', x: move.x, y: move.y, direction: move.direction.key) do |data|
|
181
|
+
move.hints.each do |hint|
|
182
|
+
data.hint(content: hint.content)
|
243
183
|
end
|
244
184
|
end
|
245
|
-
move.hints.each do |hint|
|
246
|
-
data.hint(content: hint.content)
|
247
|
-
end
|
248
185
|
builder.target!
|
249
186
|
end
|
250
187
|
|