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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 5eac2f41124e1ee9f61d00d598895df4dd553c2c
4
- data.tar.gz: 3d2d15d84c9363fe171a7a127998b7b673388508
2
+ SHA256:
3
+ metadata.gz: 5f9c0fc11c46d62fc8f289da4a17ec7b1bde4af3b8b4a678a213f4ad663f2ff2
4
+ data.tar.gz: aac39e99052feb22f58929f8a7271ff4ce412910fd4bdb9ee41a72bab59a4499
5
5
  SHA512:
6
- metadata.gz: f5ab4676a7e0a78e5c3a099a03b3983bcbb5a3a9a32386a89dff18b85f9a266ef896206541cedcf6bd870d2eaf416cc6191f47d3643c6ab32a34b0869d6b8b47
7
- data.tar.gz: 02a1405157463238828852063aeab0da67c34ed2d2265fc21c7b830762d06d00699657db6fc0a15fae532190fe92d123c65777ae5781b44116c79e36b4061f2b
6
+ metadata.gz: 31e76449052b5ffc665bb5d4e62f26fdeda42a982aefc701e00aa22759d6467b0bbd0ef8dc50c31733d38a91ef3fa99b7a7413d7f67dffbc58145be92c5ec3e0
7
+ data.tar.gz: 32a796bf7bbf4e91163abc90bb9686bf5027ca0f669b27d3b408f62c75bc8064886f03abad706f47ecd4dcb3c6e7255e1517f53352792b6af84addeeb81f84c6
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.4.1
1
+ 2.6.5
data/.stickler.yml ADDED
@@ -0,0 +1,7 @@
1
+ linters:
2
+ rubocop:
3
+ fixer: true
4
+ shellcheck:
5
+ shell: bash
6
+ fixers:
7
+ enable: true
data/RELEASES.md CHANGED
@@ -1,3 +1,7 @@
1
+ = 20.2.0
2
+
3
+ First version for game "Hive"
4
+
1
5
  = 19.1.0
2
6
 
3
7
  - winning condition is now set when changing a gamestate with Move#perform!
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.possible_moves.sample
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/field_type'
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/move'
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/line_direction'
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 bestehend aus 10x10 Feldern.
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 Koordinaten im Array gespeichert.
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
- (0..9).to_a.each do |y|
21
- @fields[y] = []
22
- (0..9).to_a.each do |x|
23
- @fields[y][x] = Field.new(x, y, FieldType::EMPTY)
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
- fields.each_with_index do |row, y|
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.y][field.x] = 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. 0..9, wobei Spalte 0 ganz links und Spalte 9 ganz rechts liegt.
58
- # @param y [Integer] Die Y-Koordinate des Feldes. 0..9, wobei Zeile 0 ganz unten und Zeile 9 ganz oben liegt.
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.negative? || y.negative?
63
- fields.dig(y, x) # NOTE that #dig requires ruby 2.3+
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 [Coordinates] X- und Y-Koordinate als Paar, sonst wie bei {Board#field}.
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 eines angegebenen Typs des Spielbrettes.
85
+ # Liefert alle Felder die dem Spieler mit der gegebenen Farbe gehoeren
77
86
  #
78
- # @param field_type [FieldType] Der Typ, dessen Felder zurückgegeben werden sollen.
79
- # @return [Array<Field>] Alle Felder des angegebenen Typs die das Spielbrett enthält.
80
- def fields_of_type(field_type)
81
- fields.flatten.select{ |f| f.type == field_type }
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
- fields.reverse.map do |row|
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 acht möglichen Bewegungsrichtungen auf dem Spielbrett. Die Richtungen sind:
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 [Coordinates] Das zu verschiebende Koordinatenpaar.
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(coordinates, distance = 1)
34
- case key
35
- when :UP
36
- Coordinates.new(coordinates.x, coordinates.y + distance)
37
- when :UP_RIGHT
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
- Coordinates.new(coordinates.x + distance, coordinates.y)
41
- when :DOWN_RIGHT
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
- Coordinates.new(coordinates.x - distance, coordinates.y)
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
- Coordinates.new(coordinates.x - distance, coordinates.y + distance)
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] type
8
- # @return [FieldType] der Typ des Feldes
9
- attr_accessor :type
10
- # @!attribute [r] x
11
- # @return [Integer] die X-Koordinate des Feldes (0 bis 9, 0 ist ganz links, 9 ist ganz rechts)
12
- attr_reader :x
13
- # @!attribute [r] y
14
- # @return [Integer] die Y-Koordinate des Feldes (0 bis 9, 0 ist ganz unten, 9 ist ganz oben)
15
- attr_reader :y
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
- def initialize(x, y, type)
23
- @type = type
24
- @x = x
25
- @y = y
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
- type == other.type &&
32
- x == other.x &&
33
- y == other.y
32
+ coordinates == other.coordinates &&
33
+ obstructed == other.obstructed &&
34
+ pieces == other.pieces
34
35
  end
35
36
 
36
- # @return [Coordinates] Die Koordinaten des Feldes als Koordinatenpaar.
37
- def coordinates
38
- Coordinates.new(x, y)
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 (#{x},#{y}), Typ = #{type}"
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