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