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
@@ -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
|