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