software_challenge_client 1.2.1 → 19.0.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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +3 -0
  3. data/README.md +64 -26
  4. data/example/client.rb +1 -71
  5. data/example/main.rb +1 -1
  6. data/lib/software_challenge_client.rb +4 -2
  7. data/lib/software_challenge_client/board.rb +66 -19
  8. data/lib/software_challenge_client/client_interface.rb +10 -5
  9. data/lib/software_challenge_client/condition.rb +2 -2
  10. data/lib/software_challenge_client/coordinates.rb +17 -0
  11. data/lib/software_challenge_client/debug_hint.rb +8 -4
  12. data/lib/software_challenge_client/direction.rb +53 -0
  13. data/lib/software_challenge_client/field.rb +26 -12
  14. data/lib/software_challenge_client/field_type.rb +25 -19
  15. data/lib/software_challenge_client/game_rule_logic.rb +230 -0
  16. data/lib/software_challenge_client/game_state.rb +45 -191
  17. data/lib/software_challenge_client/invalid_move_exception.rb +6 -8
  18. data/lib/software_challenge_client/line.rb +126 -0
  19. data/lib/software_challenge_client/line_direction.rb +15 -0
  20. data/lib/software_challenge_client/logging.rb +3 -2
  21. data/lib/software_challenge_client/move.rb +51 -38
  22. data/lib/software_challenge_client/network.rb +3 -1
  23. data/lib/software_challenge_client/player.rb +0 -39
  24. data/lib/software_challenge_client/player_color.rb +23 -13
  25. data/lib/software_challenge_client/protocol.rb +20 -83
  26. data/lib/software_challenge_client/runner.rb +2 -1
  27. data/lib/software_challenge_client/util/constants.rb +8 -5
  28. data/lib/software_challenge_client/version.rb +1 -1
  29. data/software_challenge_client.gemspec +2 -0
  30. metadata +24 -8
  31. data/lib/software_challenge_client/action.rb +0 -217
  32. data/lib/software_challenge_client/card_type.rb +0 -13
  33. data/lib/software_challenge_client/field_unavailable_exception.rb +0 -17
  34. data/lib/software_challenge_client/game_rules.rb +0 -376
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f17d0f5a06ce3da01d2700ea50e20e6f8ccb9d4f
4
- data.tar.gz: 85f493205e07e2ec8c4e66b61a51da5b4a2e43dd
3
+ metadata.gz: 42f624c3d6632a5451ccd927967699ce2c663340
4
+ data.tar.gz: 5e8540a594b7a896535c64a617c80e50d801c9db
5
5
  SHA512:
6
- metadata.gz: ab46b529b1874eecfc1c9a3449d7e0c70734d06247004c473b183054c31e1251fabcd3be7877a4f80b6ecf3412766a0eebcb009dd852d50dcdf27fb68a397ee3
7
- data.tar.gz: cb7cd641de2dbdf3caf1b60fb5058a00436ea985b69866938dac4988accc5e833ce1e69fd094b17d00d877e43460757480ae8b103fd104870002dc14eeec23a7
6
+ metadata.gz: efbae299e27a8a7db3aa5cf706c2e0c4a987647822103d59394ebee3bbb6367d7f0308cf8173c519a81a228277ab0f1ca0e78cd8b914e6afef681eb5d7a8a309
7
+ data.tar.gz: 2b35430ade3b3cb6edfda0c8f29b5b6dc131f5f278da1f88a4cbec9a1f06b336d7fa2fb457eefd8ac758f6086021b12e10767eb6364494823477ea560e7afb93
data/Dockerfile ADDED
@@ -0,0 +1,3 @@
1
+ FROM ruby:2.4.2
2
+
3
+ RUN gem install --no-document software_challenge_client
data/README.md CHANGED
@@ -1,47 +1,89 @@
1
- # Software Challenge Client
1
+ # Software-Challenge Client
2
2
 
3
3
  This gem includes everything to build a client for the coding
4
4
  competition [Software-Challenge](http://www.software-challenge.de).
5
5
 
6
+ ------------------------------------------------------------------------
7
+
8
+ _Language:_ Most documentation will be in german language, because it is intended
9
+ to be used by pupils taking part in the Software-Challenge programming
10
+ competition. Only internal documentation is in english.
11
+
12
+ ------------------------------------------------------------------------
13
+
14
+ _Sprache:_ Ein Grossteil der Dokumentation ist in deutscher Sprache verfasst, da
15
+ sie dazu gedacht ist, von am Programmierwettbewerb Software-Challenge
16
+ teilnehmenden Schülerinnen und Schülern gelesen zu werden. Interne Dokumentation
17
+ ist weiterhin in englisch.
18
+
19
+ ------------------------------------------------------------------------
20
+
6
21
  ## Installation
7
22
 
8
- Add this line to your application's Gemfile:
23
+ Um die Software-Challenge Bibliothek in deinem Computerspieler zu verwenden, füge folgende Zeile in das Gemfile deines Projektes ein:
9
24
 
10
- ```ruby
11
- gem 'software_challenge_client'
12
- ```
25
+ gem 'software_challenge_client'
13
26
 
14
- And then execute:
27
+ Installiere das Gem dann mit dem Befehl:
15
28
 
16
29
  $ bundle
17
30
 
18
- Or install it yourself as:
31
+ Oder installiere das Gem ohne ein Gemfile mit:
19
32
 
20
33
  $ gem install software_challenge_client
21
34
 
22
- ## Usage
35
+ ## Verwendung
36
+
37
+ Ein Beispielprojekt zur Verwendung der Bibliothek findet man im Verzeichnis `example` ([Beipspielprojekt auf GitHub](https://github.com/CAU-Kiel-Tech-Inf/socha_ruby_client/tree/master/example)).
38
+
39
+ Du kannst den Beispielclient mittels
40
+
41
+ ruby main.rb
42
+
43
+ in einer Konsole ausführen (dazu musst du dich im Verzeichnis `example` befinden).
44
+
45
+ Damit dies funktioniert, muss das Gem bereits wie oben beschrieben installiert
46
+ sein und es muss ein Spielserver auf eine Verbindung warten (also zum Beispiel
47
+ ein Spiel mit einem manuell gestarteten Spieler in der grafischen Oberfläche
48
+ angelegt worden sein).
49
+
50
+ Neben Beiwerk wie dem Initialisieren der Verbindung zum Spielserver und
51
+ Verarbeiten der Startparameter (was beides in `main.rb` des Beispielprojektes
52
+ passiert), musst du nur eine Klasse implementieren, um einen lauffähigen
53
+ Computerspieler zu haben (`client.rb` im Beispielprojekt):
54
+
55
+ require 'software_challenge_client'
56
+
57
+ class Client < ClientInterface
58
+ include Logging
23
59
 
24
- See the example client in the example directory.
60
+ attr_accessor :gamestate
25
61
 
26
- You can execute the example client by entering
62
+ def initialize(log_level)
63
+ logger.level = log_level
64
+ logger.info 'Einfacher Spieler wurde erstellt.'
65
+ end
27
66
 
28
- ```console
29
- ruby main.rb
30
- ```
67
+ # gets called, when it's your turn
68
+ def move_requested
69
+ logger.info "Spielstand: #{gamestate.points_for_player(gamestate.current_player)} - #{gamestate.points_for_player(gamestate.other_player)}"
70
+ move = best_move
71
+ logger.debug "Zug gefunden: #{move}" unless move.nil?
72
+ move
73
+ end
31
74
 
32
- in a shell (while being in the example directory). Note that the
33
- `software_challenge_client` gem needs to be installed for this to work and a
34
- server waiting for a manual client has to be running.
75
+ def best_move
76
+ gamestate.possible_moves.sample
77
+ end
78
+ end
35
79
 
36
- ## Documentation
80
+ ## Generating the Documentation
37
81
 
38
82
  Code documentation can be generated using YARD in the project root (source code
39
83
  needs to be checked out and `bundle` has to be executed,
40
84
  see [Installation](#installation)):
41
85
 
42
- ```console
43
- yard
44
- ```
86
+ yard
45
87
 
46
88
  After generation, the docs can be found in the `doc` directory. Start at
47
89
  `index.html`.
@@ -52,15 +94,11 @@ on
52
94
 
53
95
  When updating the docs, you may use
54
96
 
55
- ```console
56
- yard server --reload
57
- ```
97
+ yard server --reload
58
98
 
59
99
  or inside a docker container
60
100
 
61
- ```console
62
- yard server --reload --bind 0.0.0.0
63
- ```
101
+ yard server --reload --bind 0.0.0.0
64
102
 
65
103
  to get a live preview of them at [http://localhost:8808](http://localhost:8808).
66
104
 
data/example/client.rb CHANGED
@@ -8,9 +8,6 @@ class Client < ClientInterface
8
8
 
9
9
  attr_accessor :gamestate
10
10
 
11
- # Anzahl der Spielfelder
12
- NUM_FIELDS = 65
13
-
14
11
  def initialize(log_level)
15
12
  logger.level = log_level
16
13
  logger.info 'Einfacher Spieler wurde erstellt.'
@@ -25,73 +22,6 @@ class Client < ClientInterface
25
22
  end
26
23
 
27
24
  def best_move
28
- possible_moves = gamestate.possible_moves # Enthält mindestens ein Element
29
- salad_moves = []
30
- winning_moves = []
31
- selected_moves = []
32
-
33
- index = gamestate.current_player.index
34
- possible_moves.each do |move|
35
- move.actions.each do |action|
36
- case action.type
37
- when :advance
38
- target_field_index = action.distance + index
39
- if target_field_index == NUM_FIELDS - 1
40
- winning_moves << move
41
- elsif gamestate.field(target_field_index).type == FieldType::SALAD
42
- salad_moves << move
43
- else
44
- selected_moves << move
45
- end
46
- when :card
47
- if action.card_type == CardType::EAT_SALAD
48
- # Zug auf Hasenfeld und danach Salatkarte
49
- salad_moves << move
50
- # Muss nicht zusätzlich ausgewählt werden, wurde schon durch Advance ausgewählt
51
- end
52
- when :exchange_carrots
53
- if action.value == 10 &&
54
- gamestate.current_player.carrots < 30 &&
55
- index < 40 &&
56
- !gamestate.current_player.last_non_skip_action.instance_of?(ExchangeCarrots)
57
- # Nehme nur Karotten auf, wenn weniger als 30 und nur am Anfang und nicht zwei
58
- # mal hintereinander
59
- selected_moves << move
60
- elsif action.value == -10 &&
61
- gamestate.current_player.carrots > 30 &&
62
- index >= 40
63
- # Abgeben von Karotten ist nur am Ende sinnvoll
64
- selected_moves << move
65
- end
66
- when :fall_back
67
- if index > 56 && # letztes Salatfeld
68
- gamestate.current_player.salads > 0
69
- # Falle nur am Ende (index > 56) zurück, außer du musst noch einen Salat loswerden
70
- selected_moves << move
71
- elsif index <= 56 &&
72
- gamestate.previous_field_of_type(FieldType::HEDGEHOG, index) &&
73
- index - gamestate.previous_field_of_type(FieldType::HEDGEHOG, index).index < 5
74
- # Falle zurück, falls sich Rückzug lohnt (nicht zu viele Karotten aufnehmen)
75
- selected_moves << move
76
- end
77
- else
78
- # Füge Salatessen oder Skip hinzu
79
- selected_moves << move
80
- end
81
- end
82
- end
83
-
84
- if !winning_moves.empty?
85
- logger.info("Waehle Gewinnzug")
86
- winning_moves.sample
87
- elsif !salad_moves.empty?
88
- # es gibt die Möglichkeit einen Salat zu essen
89
- logger.info("Waehle Zug zum Salatessen")
90
- salad_moves.sample
91
- elsif !selected_moves.empty?
92
- selected_moves.sample
93
- else
94
- possible_moves.sample
95
- end
25
+ gamestate.possible_moves.sample
96
26
  end
97
27
  end
data/example/main.rb CHANGED
@@ -39,4 +39,4 @@ opt_parser.parse!(ARGV)
39
39
 
40
40
  client = Client.new(Logger::DEBUG)
41
41
  runner = Runner.new(options.host, options.port, client, options.reservation)
42
- runner.start()
42
+ runner.start
@@ -3,7 +3,6 @@ module SoftwareChallengeClient
3
3
  require 'software_challenge_client/version'
4
4
  require 'software_challenge_client/logging'
5
5
  require 'software_challenge_client/invalid_move_exception'
6
- require 'software_challenge_client/field_unavailable_exception'
7
6
  require 'software_challenge_client/board'
8
7
  require 'software_challenge_client/client_interface'
9
8
  require 'software_challenge_client/condition'
@@ -17,5 +16,8 @@ module SoftwareChallengeClient
17
16
  require 'software_challenge_client/player_color'
18
17
  require 'software_challenge_client/protocol'
19
18
  require 'software_challenge_client/runner'
20
- require 'software_challenge_client/game_rules'
19
+ require 'software_challenge_client/game_rule_logic'
20
+ require 'software_challenge_client/direction'
21
+ require 'software_challenge_client/line_direction'
22
+ require 'software_challenge_client/coordinates'
21
23
  end
@@ -1,46 +1,93 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
2
4
  require_relative './util/constants'
3
5
  require_relative 'game_state'
4
- require_relative 'player'
5
6
  require_relative 'field_type'
6
7
  require_relative 'field'
7
8
 
8
- # Ein Spielbrett bestehend aus 65 Feldern.
9
+ # Ein Spielbrett bestehend aus 10x10 Feldern.
9
10
  class Board
10
11
  # @!attribute [r] fields
11
12
  # @note Besser über die {#field} Methode auf Felder zugreifen.
12
- # @return [Array<Field>] Ein Feld wird an der Position entsprechend seines
13
- # Index im Array gespeichert.
13
+ # @return [Array<Array<Field>>] Ein Feld wird an der Position entsprechend
14
+ # seiner Koordinaten im Array gespeichert.
14
15
  attr_reader :fields
15
16
 
16
- # Initializes the board
17
+ # Erstellt ein neues leeres Spielbrett.
17
18
  def initialize
18
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)
24
+ end
25
+ end
19
26
  end
20
27
 
21
- def to_s
22
- fields.map { |f| f.type.value }.join(' ')
23
- end
24
-
28
+ # Vergleicht zwei Spielbretter. Gleichheit besteht, wenn zwei Spielbretter die
29
+ # gleichen Felder enthalten.
25
30
  def ==(other)
26
- fields.each_with_index do |field, index|
27
- return false if field != other.field(index)
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
28
35
  end
29
36
  true
30
37
  end
31
38
 
39
+ # Fügt ein Feld dem Spielbrett hinzu. Das übergebene Feld ersetzt das an den Koordinaten bestehende Feld.
40
+ #
41
+ # @param field [Field] Das einzufügende Feld.
32
42
  def add_field(field)
33
- @fields[field.index] = 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
34
53
  end
35
54
 
36
55
  # Zugriff auf die Felder des Spielfeldes
37
56
  #
38
- # @param index [Integer] Der Index des Feldes
39
- # @return [Field] Das Feld mit dem gegebenen Index. Falls das Feld nicht
40
- # exisitert (weil der Index ausserhalb von 0..64 liegt), wird ein neues
41
- # Feld vom Typ INVALID zurückgegeben.
42
- def field(index)
43
- return Field.new(FieldType::INVALID, index) if index.negative?
44
- fields.fetch(index, Field.new(FieldType::INVALID, index))
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.
61
+ def field(x, y)
62
+ return nil if x.negative? || y.negative?
63
+ fields.dig(y, x) # NOTE that #dig requires ruby 2.3+
64
+ end
65
+
66
+ # Zugriff auf die Felder des Spielfeldes über ein Koordinaten-Paar.
67
+ #
68
+ # @param coordinates [Coordinates] X- und Y-Koordinate als Paar, sonst wie bei {Board#field}.
69
+ # @return [Field] Wie bei {Board#field}.
70
+ #
71
+ # @see #field
72
+ def field_at(coordinates)
73
+ field(coordinates.x, coordinates.y)
74
+ end
75
+
76
+ # Liefert alle Felder eines angegebenen Typs des Spielbrettes.
77
+ #
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 }
45
82
  end
83
+
84
+ # Gibt eine textuelle Repräsentation des Spielbrettes aus. Hier steht R für
85
+ # einen roten Fisch, B für einen blauen, ~ für ein leeres Feld und O für ein
86
+ # Kraken-Feld.
87
+ def to_s
88
+ fields.reverse.map do |row|
89
+ row.map { |f| f.type.value }.join(' ')
90
+ end.join("\n")
91
+ end
92
+
46
93
  end
@@ -1,12 +1,17 @@
1
- # encoding: UTF-8
1
+ # encoding: utf-8
2
2
 
3
- # The interface a client should implement to work with the gem.
3
+ # Das Interface sollte von einem Client implementiert werden, damit er über das
4
+ # Gem an einem Spiel teilnehmen kann.
4
5
  class ClientInterface
5
- # Is updated by the gem, when a new gamestate is received from the server.
6
+ # Wird automatisch aktualisiert und ist immer der Spielzustand des aktuellen Zuges.
6
7
  attr_accessor :gamestate
7
8
 
8
- # Is called when the server requests a move from the client.
9
- # @return [Move] Needs to return a valid move.
9
+ # Wird aufgerufen, wenn der Client einen Zug machen soll. Dies ist der
10
+ # Einstiegspunkt für die eigentliche Logik des Computerspielers. Er muss auf
11
+ # Basis des Spielzustandes entscheiden, welchen Zug er machen möchte und diese
12
+ # zurückgeben.
13
+ #
14
+ # @return [Move] Ein für den aktuellen Spielzustand gültiger Spielzug.
10
15
  def move_requested
11
16
  raise 'Not yet implemented'
12
17
  end
@@ -1,10 +1,10 @@
1
1
  # encoding: UTF-8
2
2
  require_relative 'player'
3
3
 
4
- # Represents the winning condition received from the server when the game ended.
4
+ # Das Ergebnis eines Spieles. Ist im `GameState#condition` zu finden, wenn das Spiel beendet wurde.
5
5
  class Condition
6
6
  # @!attribute [r] winner
7
- # @return [Player] winning player
7
+ # @return [Player] Spieler, der das Spiel gewonnen hat.
8
8
  attr_reader :winner
9
9
 
10
10
  # Initializes the winning Condition with a player
@@ -0,0 +1,17 @@
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,11 +1,15 @@
1
- # encoding: UTF-8
2
- # A debug hint, that can be added to a move
1
+ # encoding: utf-8
2
+
3
+ # Ein Hinweis, der zu einem Zug hinzugefügt werden kann. Z.B. zu
4
+ # Diagnosezwecken. Der Hinweis wird in der grafischen Oberfläche angezeigt und
5
+ # in Replay-Dateien gespeichert.
3
6
  class DebugHint
4
7
  # @!attribute [r] content
5
- # @return [String] a hint
8
+ # @return [String] Der Text des Hinweises.
6
9
  attr_reader :content
7
10
 
8
- # @param content [Object] of the hint, will be converted to a string
11
+ # Erstellt einen neuen Hinweis.
12
+ # @param content [Object] Inhalt des Hinweises. Wird zu String konvertiert.
9
13
  def initialize(content)
10
14
  @content = content.to_s
11
15
  end