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