software_challenge_client 19.1.0 → 20.2.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 +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
@@ -1,17 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
# Ein Koordinatenpaar für ein zweidimensionales Koordinatensystem.
|
5
|
-
class Coordinates
|
6
|
-
|
7
|
-
# X-Koordinate
|
8
|
-
attr_reader :x
|
9
|
-
# Y-Koordinate
|
10
|
-
attr_reader :y
|
11
|
-
|
12
|
-
# Erstellt ein neues Koordinatenpaar aus X- und Y-Koordinate.
|
13
|
-
def initialize(x, y)
|
14
|
-
@x = x
|
15
|
-
@y = y
|
16
|
-
end
|
17
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'typesafe_enum'
|
4
|
-
# Der Typ eines Feldes des Spielbrettes. Es gibt folgende Typen:
|
5
|
-
# - EMPTY
|
6
|
-
# - RED
|
7
|
-
# - BLUE
|
8
|
-
# - OBSTRUCTED
|
9
|
-
#
|
10
|
-
# Zugriff z.B. mit FieldType::RED
|
11
|
-
class FieldType < TypesafeEnum::Base
|
12
|
-
new :EMPTY, '~'
|
13
|
-
new :RED, 'R'
|
14
|
-
new :BLUE, 'B'
|
15
|
-
new :OBSTRUCTED, 'O'
|
16
|
-
|
17
|
-
# @param field_type [FieldType] Der Feldtyp, zu dem die Spielerfarbe ermittelt werden soll.
|
18
|
-
# @return [PlayerColor] Die zum Feldtyp gehörende Spielerfarbe, also PlayerColor::RED für FieldType::RED und PlayerColor::BLUE für FieldType::BLUE. In allen anderen Fällen PlayerColor::NONE.
|
19
|
-
# @see PlayerColor#field_type
|
20
|
-
def self.player_color(field_type)
|
21
|
-
case field_type
|
22
|
-
when FieldType::RED
|
23
|
-
PlayerColor::RED
|
24
|
-
when FieldType::BLUE
|
25
|
-
PlayerColor::BLUE
|
26
|
-
else
|
27
|
-
PlayerColor::NONE
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
@@ -1,126 +0,0 @@
|
|
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
|
@@ -1,84 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
require_relative 'debug_hint'
|
3
|
-
|
4
|
-
# Ein Spielzug. Er ist definiert durch das Koordinatenpaar des Ausgangsfeldes (ein Fisch des Spielers, der den Zug machen will) und eine Bewegungsrichtung.
|
5
|
-
class Move
|
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
|
15
|
-
#
|
16
|
-
# @return [Direction] Die Richtung, in die bewegt werden soll.
|
17
|
-
attr_reader :direction
|
18
|
-
|
19
|
-
# @!attribute [r] hints
|
20
|
-
# @return [Array<DebugHint>] Hinweise, die an den Zug angeheftet werden sollen. Siehe {DebugHint}.
|
21
|
-
attr_reader :hints
|
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 = []
|
32
|
-
end
|
33
|
-
|
34
|
-
# @param hint [DebugHint]
|
35
|
-
def add_hint(hint)
|
36
|
-
@hints.push(hint)
|
37
|
-
end
|
38
|
-
|
39
|
-
def ==(other)
|
40
|
-
x == other.x && y == other.y && direction == other.direction
|
41
|
-
end
|
42
|
-
|
43
|
-
def to_s
|
44
|
-
"Move: (#{x},#{y}) #{direction}"
|
45
|
-
end
|
46
|
-
|
47
|
-
# @return [Coordinates] Die Koordinaten des Ausgangsfeldes des Zuges als Koordinatenpaar.
|
48
|
-
def from_field
|
49
|
-
Coordinates.new(x, y)
|
50
|
-
end
|
51
|
-
|
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, gamestate.current_player_color)
|
57
|
-
end
|
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]
|
61
|
-
def perform!(gamestate)
|
62
|
-
if GameRuleLogic.valid_move?(self, gamestate.board, gamestate.current_player_color)
|
63
|
-
type = gamestate.board.field(x, y).type
|
64
|
-
target = GameRuleLogic.move_target(self, gamestate.board)
|
65
|
-
gamestate.board.change_field(x, y, FieldType::EMPTY)
|
66
|
-
gamestate.board.change_field(target.x, target.y, type)
|
67
|
-
else
|
68
|
-
raise InvalidMoveException.new('Invalid move', self)
|
69
|
-
end
|
70
|
-
# change the state to the next turn
|
71
|
-
gamestate.last_move = self
|
72
|
-
gamestate.turn += 1
|
73
|
-
gamestate.switch_current_player
|
74
|
-
gamestate.condition = GameRuleLogic.winning_condition(gamestate)
|
75
|
-
end
|
76
|
-
|
77
|
-
|
78
|
-
# Ermittelt die Koordinaten des Zielfeldes des Zuges mit einer gegebenen Zugweite.
|
79
|
-
# @param speed [Integer] Die Zugweite. Entspricht normalerweise der Anzahl der Fische auf der Bewegungslinie.
|
80
|
-
# @return [Coordinates] Koordinaten des Zielfeldes. Eventuell ausserhalb des Spielbrettes.
|
81
|
-
def target_field(speed)
|
82
|
-
direction.translate(from_field, speed)
|
83
|
-
end
|
84
|
-
end
|