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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 5f9c0fc11c46d62fc8f289da4a17ec7b1bde4af3b8b4a678a213f4ad663f2ff2
|
4
|
+
data.tar.gz: aac39e99052feb22f58929f8a7271ff4ce412910fd4bdb9ee41a72bab59a4499
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 31e76449052b5ffc665bb5d4e62f26fdeda42a982aefc701e00aa22759d6467b0bbd0ef8dc50c31733d38a91ef3fa99b7a7413d7f67dffbc58145be92c5ec3e0
|
7
|
+
data.tar.gz: 32a796bf7bbf4e91163abc90bb9686bf5027ca0f669b27d3b408f62c75bc8064886f03abad706f47ecd4dcb3c6e7255e1517f53352792b6af84addeeb81f84c6
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.6.5
|
data/.stickler.yml
ADDED
data/RELEASES.md
CHANGED
data/example/client.rb
CHANGED
@@ -16,12 +16,23 @@ class Client < ClientInterface
|
|
16
16
|
# gets called, when it's your turn
|
17
17
|
def move_requested
|
18
18
|
logger.info "Spielstand: #{gamestate.points_for_player(gamestate.current_player)} - #{gamestate.points_for_player(gamestate.other_player)}"
|
19
|
+
logger.debug "Board: #{gamestate.board.to_s}"
|
19
20
|
move = best_move
|
20
21
|
logger.debug "Zug gefunden: #{move}" unless move.nil?
|
21
22
|
move
|
22
23
|
end
|
23
24
|
|
24
25
|
def best_move
|
25
|
-
gamestate.
|
26
|
+
#gamestate.board.add_field(Field.new(5, 0))
|
27
|
+
logger.debug "Berechne zuege fuer Board #{gamestate.board.to_s}"
|
28
|
+
logger.debug "Felder"
|
29
|
+
gamestate.board.field_list.each do |f|
|
30
|
+
if !f.empty?
|
31
|
+
logger.debug "Feld (#{f.x}, #{f.y}) #{f.obstructed ? 'OO' : f.pieces.last.to_s}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
possible_moves = GameRuleLogic.possible_moves(gamestate)
|
35
|
+
logger.debug "#{possible_moves.size} moegliche Zuege gefunden"
|
36
|
+
possible_moves.sample
|
26
37
|
end
|
27
38
|
end
|
@@ -8,9 +8,12 @@ module SoftwareChallengeClient
|
|
8
8
|
require 'software_challenge_client/condition'
|
9
9
|
require 'software_challenge_client/debug_hint'
|
10
10
|
require 'software_challenge_client/field'
|
11
|
-
require 'software_challenge_client/
|
11
|
+
require 'software_challenge_client/piece'
|
12
|
+
require 'software_challenge_client/piece_type'
|
12
13
|
require 'software_challenge_client/game_state'
|
13
|
-
require 'software_challenge_client/
|
14
|
+
require 'software_challenge_client/set_move'
|
15
|
+
require 'software_challenge_client/drag_move'
|
16
|
+
require 'software_challenge_client/skip_move'
|
14
17
|
require 'software_challenge_client/network'
|
15
18
|
require 'software_challenge_client/player'
|
16
19
|
require 'software_challenge_client/player_color'
|
@@ -18,6 +21,5 @@ module SoftwareChallengeClient
|
|
18
21
|
require 'software_challenge_client/runner'
|
19
22
|
require 'software_challenge_client/game_rule_logic'
|
20
23
|
require 'software_challenge_client/direction'
|
21
|
-
require 'software_challenge_client/
|
22
|
-
require 'software_challenge_client/coordinates'
|
24
|
+
require 'software_challenge_client/cube_coordinates'
|
23
25
|
end
|
@@ -3,69 +3,78 @@
|
|
3
3
|
|
4
4
|
require_relative './util/constants'
|
5
5
|
require_relative 'game_state'
|
6
|
-
require_relative 'field_type'
|
7
6
|
require_relative 'field'
|
8
7
|
|
9
|
-
# Ein Spielbrett
|
8
|
+
# Ein Spielbrett fuer Hive
|
10
9
|
class Board
|
10
|
+
|
11
|
+
include Constants
|
11
12
|
# @!attribute [r] fields
|
12
13
|
# @note Besser über die {#field} Methode auf Felder zugreifen.
|
13
14
|
# @return [Array<Array<Field>>] Ein Feld wird an der Position entsprechend
|
14
|
-
# seiner
|
15
|
+
# seiner x und y CubeCoordinates im Array gespeichert.
|
15
16
|
attr_reader :fields
|
16
17
|
|
18
|
+
def self.field_amount(radius)
|
19
|
+
return 1 if radius == 1
|
20
|
+
(radius - 1) * 6 + Board.field_amount(radius - 1)
|
21
|
+
end
|
22
|
+
|
23
|
+
FIELD_AMOUNT = Board.field_amount((BOARD_SIZE + 1)/2)
|
24
|
+
|
17
25
|
# Erstellt ein neues leeres Spielbrett.
|
18
|
-
def initialize
|
19
|
-
@fields =
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
26
|
+
def initialize(fields = [])
|
27
|
+
@fields = Board.empty_game_field
|
28
|
+
fields.each{ |f| add_field(f) }
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.empty_game_field
|
32
|
+
fields = []
|
33
|
+
(-SHIFT..SHIFT).to_a.each do |x|
|
34
|
+
fields[x + SHIFT] ||= []
|
35
|
+
([-SHIFT, -x-SHIFT].max..[SHIFT, -x+SHIFT].min).to_a.each do |y|
|
36
|
+
fields[x + SHIFT][y + SHIFT] = Field.new(x, y)
|
24
37
|
end
|
25
38
|
end
|
39
|
+
fields
|
40
|
+
end
|
41
|
+
|
42
|
+
def clear
|
43
|
+
@fields = []
|
44
|
+
end
|
45
|
+
|
46
|
+
def field_list
|
47
|
+
@fields.flatten.select{ |e| !e.nil? }
|
26
48
|
end
|
27
49
|
|
28
50
|
# Vergleicht zwei Spielbretter. Gleichheit besteht, wenn zwei Spielbretter die
|
29
51
|
# gleichen Felder enthalten.
|
30
52
|
def ==(other)
|
31
|
-
|
32
|
-
row.each_with_index do |field, x|
|
33
|
-
return false if field != other.field(x, y)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
true
|
53
|
+
field_list == other.field_list
|
37
54
|
end
|
38
55
|
|
39
56
|
# Fügt ein Feld dem Spielbrett hinzu. Das übergebene Feld ersetzt das an den Koordinaten bestehende Feld.
|
40
57
|
#
|
41
58
|
# @param field [Field] Das einzufügende Feld.
|
42
59
|
def add_field(field)
|
43
|
-
@fields[field.
|
44
|
-
end
|
45
|
-
|
46
|
-
# Ändert den Typ eines bestimmten Feldes des Spielbrettes.
|
47
|
-
#
|
48
|
-
# @param x [Integer] Die X-Koordinate des zu ändernden Feldes. 0..9, wobei Spalte 0 ganz links und Spalte 9 ganz rechts liegt.
|
49
|
-
# @param y [Integer] Die Y-Koordinate des zu ändernden Feldes. 0..9, wobei Zeile 0 ganz unten und Zeile 9 ganz oben liegt.
|
50
|
-
# @param type [FieldType] Der neue Typ des Feldes.
|
51
|
-
def change_field(x, y, type)
|
52
|
-
@fields[y][x].type = type
|
60
|
+
@fields[field.x + SHIFT][field.y + SHIFT] = field
|
53
61
|
end
|
54
62
|
|
55
63
|
# Zugriff auf die Felder des Spielfeldes
|
56
64
|
#
|
57
|
-
# @param x [Integer] Die X-Koordinate des Feldes.
|
58
|
-
# @param y [Integer] Die Y-Koordinate des Feldes.
|
59
|
-
# @return [Field] Das Feld mit den gegebenen Koordinaten. Falls das Feld nicht
|
60
|
-
# exisitert (weil die Koordinaten ausserhalb von (0,0)..(9,9) liegen), wird nil zurückgegeben.
|
65
|
+
# @param x [Integer] Die X-Koordinate des Feldes.
|
66
|
+
# @param y [Integer] Die Y-Koordinate des Feldes.
|
67
|
+
# @return [Field] Das Feld mit den gegebenen Koordinaten. Falls das Feld nicht exisitert, wird nil zurückgegeben.
|
61
68
|
def field(x, y)
|
62
|
-
return nil if x
|
63
|
-
fields.dig(
|
69
|
+
return nil if (x < -SHIFT) || (y < -SHIFT)
|
70
|
+
fields.dig(x + SHIFT, y + SHIFT) # NOTE that #dig requires ruby 2.3+
|
64
71
|
end
|
65
72
|
|
66
73
|
# Zugriff auf die Felder des Spielfeldes über ein Koordinaten-Paar.
|
67
74
|
#
|
68
|
-
# @param coordinates [
|
75
|
+
# @param coordinates [CubeCoordinates] X- und Y-Koordinate als Paar, sonst wie
|
76
|
+
# bei {Board#field}.
|
77
|
+
#
|
69
78
|
# @return [Field] Wie bei {Board#field}.
|
70
79
|
#
|
71
80
|
# @see #field
|
@@ -73,21 +82,31 @@ class Board
|
|
73
82
|
field(coordinates.x, coordinates.y)
|
74
83
|
end
|
75
84
|
|
76
|
-
# Liefert alle Felder
|
85
|
+
# Liefert alle Felder die dem Spieler mit der gegebenen Farbe gehoeren
|
77
86
|
#
|
78
|
-
# @param
|
79
|
-
# @return [Array<Field>] Alle Felder
|
80
|
-
def
|
81
|
-
|
87
|
+
# @param color [PlayerColor] Die Spielerfarbe
|
88
|
+
# @return [Array<Field>] Alle Felder der angegebenen Farbe die das Spielbrett enthält.
|
89
|
+
def fields_of_color(color)
|
90
|
+
field_list.select{ |f| f.color == color }
|
91
|
+
end
|
92
|
+
|
93
|
+
def pieces
|
94
|
+
field_list.map(&:pieces).flatten
|
95
|
+
end
|
96
|
+
|
97
|
+
def deployed_pieces(color)
|
98
|
+
pieces.select { |p| p.color == color }
|
99
|
+
end
|
100
|
+
|
101
|
+
def clone
|
102
|
+
Marshal.load(Marshal.dump(self))
|
82
103
|
end
|
83
104
|
|
84
105
|
# Gibt eine textuelle Repräsentation des Spielbrettes aus. Hier steht R für
|
85
106
|
# einen roten Fisch, B für einen blauen, ~ für ein leeres Feld und O für ein
|
86
107
|
# Kraken-Feld.
|
87
108
|
def to_s
|
88
|
-
|
89
|
-
row.map { |f| f.type.value }.join(' ')
|
90
|
-
end.join("\n")
|
109
|
+
field_list.sort_by(&:z).map{ |f| f.obstructed ? 'OO' : f.empty? ? '--' : f.pieces.last.to_s }.join
|
91
110
|
end
|
92
111
|
|
93
112
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class CubeCoordinates
|
2
|
+
|
3
|
+
attr_reader :x, :y, :z
|
4
|
+
|
5
|
+
def initialize(x, y, z = nil)
|
6
|
+
@x = x
|
7
|
+
@y = y
|
8
|
+
@z = z.nil? ? -x - y : z
|
9
|
+
throw InvalidArgumentException("sum of coordinates #{@x}, #{@y}, #{@z} have to be equal 0") if @x + @y + @z != 0
|
10
|
+
end
|
11
|
+
|
12
|
+
def ==(other)
|
13
|
+
x == other.x && y == other.y && z == other.z
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
"(#{x}, #{y}, #{z})"
|
18
|
+
end
|
19
|
+
|
20
|
+
def inspect
|
21
|
+
to_s
|
22
|
+
end
|
23
|
+
end
|
@@ -2,24 +2,20 @@
|
|
2
2
|
|
3
3
|
require 'typesafe_enum'
|
4
4
|
|
5
|
-
# Die
|
5
|
+
# Die sechs möglichen Bewegungsrichtungen auf dem Spielbrett. Die Richtungen sind:
|
6
6
|
#
|
7
|
-
# - UP
|
8
7
|
# - UP_RIGHT
|
9
8
|
# - RIGHT
|
10
9
|
# - DOWN_RIGHT
|
11
|
-
# - DOWN
|
12
10
|
# - DOWN_LEFT
|
13
11
|
# - LEFT
|
14
12
|
# - UP_LEFT
|
15
13
|
#
|
16
14
|
# Zugriff erfolgt z.B. durch Direction::UP_RIGHT.
|
17
15
|
class Direction < TypesafeEnum::Base
|
18
|
-
new :UP
|
19
16
|
new :UP_RIGHT
|
20
17
|
new :RIGHT
|
21
18
|
new :DOWN_RIGHT
|
22
|
-
new :DOWN
|
23
19
|
new :DOWN_LEFT
|
24
20
|
new :LEFT
|
25
21
|
new :UP_LEFT
|
@@ -28,26 +24,32 @@ class Direction < TypesafeEnum::Base
|
|
28
24
|
# entsprechende Richtung. Der resultierende Punkt kann ausserhalb des
|
29
25
|
# Spielbrettes liegen. Dies kann mit {GameRuleLogic#inside_bounds?} geprüft
|
30
26
|
# werden.
|
31
|
-
# @param coordinates [
|
27
|
+
# @param coordinates [CubeCoordinates] Das zu verschiebende Koordinatenpaar.
|
32
28
|
# @param distance [Integer] Um wieviele Felder in die Richtung verschoben werden soll.
|
33
|
-
def translate(
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
Coordinates.new(coordinates.x + distance, coordinates.y + distance)
|
29
|
+
def translate(start, distance = 1)
|
30
|
+
shiftX = start.x
|
31
|
+
shiftY = start.y
|
32
|
+
shiftZ = start.z
|
33
|
+
case self.key
|
39
34
|
when :RIGHT
|
40
|
-
|
41
|
-
|
42
|
-
Coordinates.new(coordinates.x + distance, coordinates.y - distance)
|
43
|
-
when :DOWN
|
44
|
-
Coordinates.new(coordinates.x, coordinates.y - distance)
|
45
|
-
when :DOWN_LEFT
|
46
|
-
Coordinates.new(coordinates.x - distance, coordinates.y - distance)
|
35
|
+
shiftX = start.x + distance
|
36
|
+
shiftY = start.y - distance
|
47
37
|
when :LEFT
|
48
|
-
|
38
|
+
shiftX = start.x - distance
|
39
|
+
shiftY = start.y + distance
|
40
|
+
when :UP_RIGHT
|
41
|
+
shiftX = start.x + distance
|
42
|
+
shiftZ = start.z - distance
|
49
43
|
when :UP_LEFT
|
50
|
-
|
44
|
+
shiftY = start.y + distance
|
45
|
+
shiftZ = start.z - distance
|
46
|
+
when :DOWN_RIGHT
|
47
|
+
shiftY = start.y - distance
|
48
|
+
shiftZ = start.z + distance
|
49
|
+
when :DOWN_LEFT
|
50
|
+
shiftX = start.x - distance
|
51
|
+
shiftZ = start.z + distance
|
51
52
|
end
|
53
|
+
return CubeCoordinates.new(shiftX, shiftY, shiftZ)
|
52
54
|
end
|
53
55
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative 'has_hints'
|
2
|
+
|
3
|
+
class DragMove
|
4
|
+
|
5
|
+
include HasHints
|
6
|
+
|
7
|
+
attr_reader :start
|
8
|
+
attr_reader :destination
|
9
|
+
|
10
|
+
def initialize(start, destination)
|
11
|
+
@start = start
|
12
|
+
@destination = destination
|
13
|
+
@hints = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
"[Move: Drag from #{start} to #{destination}]"
|
18
|
+
end
|
19
|
+
end
|
@@ -1,45 +1,82 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
-
require_relative 'field_type'
|
3
2
|
|
4
3
|
# Ein Feld des Spielfelds. Ein Spielfeld ist durch die Koordinaten eindeutig identifiziert.
|
5
4
|
# Das type Attribut gibt an, um welchen Feldtyp es sich handelt
|
6
5
|
class Field
|
7
|
-
# @!attribute [rw]
|
8
|
-
# @return [
|
9
|
-
attr_accessor :
|
10
|
-
# @!attribute [r]
|
11
|
-
# @return [
|
12
|
-
attr_reader :
|
13
|
-
# @!attribute [r]
|
14
|
-
# @return [
|
15
|
-
attr_reader :
|
6
|
+
# @!attribute [rw] pieces
|
7
|
+
# @return [Array<Piece>] Spielsteine auf dem Feld, beginnend beim untersten Stein
|
8
|
+
attr_accessor :pieces
|
9
|
+
# @!attribute [r] coordinates
|
10
|
+
# @return [CubeCoordinates] die Cube-Coordinates des Feldes
|
11
|
+
attr_reader :coordinates
|
12
|
+
# @!attribute [r] obstructed
|
13
|
+
# @return [Boolean] ob das Feld durch eine Brombeere blockiert ist
|
14
|
+
attr_reader :obstructed
|
16
15
|
|
17
16
|
# Konstruktor
|
18
17
|
#
|
19
18
|
# @param type [FieldType] Feldtyp
|
20
19
|
# @param x [Integer] X-Koordinate
|
21
20
|
# @param y [Integer] Y-Koordinate
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
@
|
21
|
+
# @param pieces [Array<Piece>] Spielsteine auf dem Feld
|
22
|
+
# @param obstructed [Boolean] Ob das Feld blockiert ist (Brombeere)
|
23
|
+
def initialize(x, y, pieces = [], obstructed = false)
|
24
|
+
@pieces = pieces
|
25
|
+
@coordinates = CubeCoordinates.new(x, y)
|
26
|
+
@obstructed = obstructed
|
26
27
|
end
|
27
28
|
|
28
29
|
# Vergleicht zwei Felder. Felder sind gleich, wenn sie gleiche Koordinaten und gleichen Typ haben.
|
29
30
|
# @return [Boolean] true bei Gleichheit, false sonst.
|
30
31
|
def ==(other)
|
31
|
-
|
32
|
-
|
33
|
-
|
32
|
+
coordinates == other.coordinates &&
|
33
|
+
obstructed == other.obstructed &&
|
34
|
+
pieces == other.pieces
|
34
35
|
end
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
|
37
|
+
def x
|
38
|
+
coordinates.x
|
39
|
+
end
|
40
|
+
|
41
|
+
def y
|
42
|
+
coordinates.y
|
43
|
+
end
|
44
|
+
|
45
|
+
def z
|
46
|
+
coordinates.z
|
47
|
+
end
|
48
|
+
|
49
|
+
def empty?
|
50
|
+
pieces.empty? && !obstructed
|
51
|
+
end
|
52
|
+
|
53
|
+
def obstructed?
|
54
|
+
obstructed
|
55
|
+
end
|
56
|
+
|
57
|
+
def add_piece(piece)
|
58
|
+
pieces.push(piece)
|
59
|
+
end
|
60
|
+
|
61
|
+
def remove_piece
|
62
|
+
pieces.pop
|
63
|
+
end
|
64
|
+
|
65
|
+
def color
|
66
|
+
pieces.last&.color
|
67
|
+
end
|
68
|
+
|
69
|
+
def has_owner
|
70
|
+
!color.nil?
|
39
71
|
end
|
40
72
|
|
41
73
|
# @return [String] Textuelle Darstellung des Feldes.
|
42
74
|
def to_s
|
43
|
-
"Feld
|
75
|
+
s = "Feld #{coordinates}, "
|
76
|
+
if obstructed?
|
77
|
+
s += 'blockiert'
|
78
|
+
else
|
79
|
+
s += "Steine: #{pieces.map(&:to_s).join(', ')}"
|
80
|
+
end
|
44
81
|
end
|
45
82
|
end
|