software_challenge_client 1.2.1 → 19.0.0

Sign up to get free protection for your applications and to get access to all the features.
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