software_challenge_client 22.1.0.1 → 23.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +15 -15
  3. data/.rspec +3 -3
  4. data/.rubocop.yml +11 -11
  5. data/.ruby-version +1 -1
  6. data/.stickler.yml +7 -7
  7. data/.vscode/launch.json +40 -40
  8. data/.vscode/settings.json +9 -9
  9. data/AUTHORS +6 -6
  10. data/CODE_OF_CONDUCT.md +13 -13
  11. data/Dockerfile +3 -3
  12. data/Gemfile +5 -5
  13. data/Guardfile +45 -45
  14. data/README.md +172 -147
  15. data/RELEASES.md +144 -140
  16. data/Rakefile +7 -7
  17. data/bin/console +15 -15
  18. data/bin/setup +7 -7
  19. data/develop.sh +3 -3
  20. data/example/client.rb +35 -35
  21. data/example/main.rb +42 -42
  22. data/example/start.bat +2 -2
  23. data/generate-authors.sh +19 -19
  24. data/lib/software_challenge_client/board.rb +149 -127
  25. data/lib/software_challenge_client/client_interface.rb +19 -19
  26. data/lib/software_challenge_client/condition.rb +27 -27
  27. data/lib/software_challenge_client/coordinates.rb +71 -45
  28. data/lib/software_challenge_client/debug_hint.rb +17 -17
  29. data/lib/software_challenge_client/direction.rb +41 -0
  30. data/lib/software_challenge_client/field.rb +70 -69
  31. data/lib/software_challenge_client/game_rule_logic.rb +206 -141
  32. data/lib/software_challenge_client/game_state.rb +57 -24
  33. data/lib/software_challenge_client/invalid_move_exception.rb +15 -15
  34. data/lib/software_challenge_client/logging.rb +26 -26
  35. data/lib/software_challenge_client/move.rb +37 -41
  36. data/lib/software_challenge_client/network.rb +126 -126
  37. data/lib/software_challenge_client/piece.rb +43 -81
  38. data/lib/software_challenge_client/player.rb +31 -31
  39. data/lib/software_challenge_client/protocol.rb +103 -54
  40. data/lib/software_challenge_client/runner.rb +36 -36
  41. data/lib/software_challenge_client/team.rb +23 -25
  42. data/lib/software_challenge_client/util/constants.rb +9 -9
  43. data/lib/software_challenge_client/version.rb +5 -5
  44. data/lib/software_challenge_client.rb +23 -25
  45. data/lib/update_client_module.sh +15 -15
  46. data/push_image_production.sh +12 -12
  47. data/release.sh +9 -9
  48. data/software_challenge_client.gemspec +41 -41
  49. metadata +3 -5
  50. data/lib/software_challenge_client/color.rb +0 -26
  51. data/lib/software_challenge_client/has_hints.rb +0 -11
  52. data/lib/software_challenge_client/piece_type.rb +0 -16
data/bin/setup CHANGED
@@ -1,7 +1,7 @@
1
- #!/bin/bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
-
5
- bundle install
6
-
7
- # Do any other automated setup that you need to do here
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
data/develop.sh CHANGED
@@ -1,3 +1,3 @@
1
- #!/bin/sh
2
- set -x # echo commands as they are executed
3
- docker run -it --rm -p 8808:8808 -v "$PWD":/usr/src/app -w /usr/src/app ruby:latest /bin/bash
1
+ #!/bin/sh
2
+ set -x # echo commands as they are executed
3
+ docker run -it --rm -p 8808:8808 -v "$PWD":/usr/src/app -w /usr/src/app ruby:latest /bin/bash
data/example/client.rb CHANGED
@@ -1,35 +1,35 @@
1
- # encoding: UTF-8
2
- # frozen_string_literal: true
3
- require 'software_challenge_client'
4
-
5
- # This is an example of a client playing the game using the software challenge
6
- # gem.
7
- class Client < ClientInterface
8
- include Logging
9
-
10
- attr_accessor :gamestate
11
-
12
- def initialize(log_level)
13
- logger.level = log_level
14
- logger.info 'Einfacher Spieler wurde erstellt.'
15
- end
16
-
17
- # gets called, when it's your turn
18
- def move_requested
19
- logger.info "Spielstand: #{gamestate.points_for_player(gamestate.current_player)} - #{gamestate.points_for_player(gamestate.other_player)}"
20
- logger.debug "Board: #{gamestate.board}"
21
- move = best_move
22
- logger.debug "Zug gefunden: #{move}" unless move.nil?
23
- move
24
- end
25
-
26
- def best_move
27
- # gamestate.board.add_field(Field.new(5, 0))
28
- logger.debug "Berechne zuege fuer Board #{gamestate.board}"
29
-
30
- # all possible moves can't be calculated in under two seconds
31
- possible_moves = GameRuleLogic.possible_moves(gamestate)
32
- logger.debug "#{possible_moves.size} moegliche Zuege gefunden"
33
- possible_moves.sample
34
- end
35
- end
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+ require 'software_challenge_client'
4
+
5
+ # This is an example of a client playing the game using the software challenge
6
+ # gem.
7
+ class Client < ClientInterface
8
+ include Logging
9
+
10
+ attr_accessor :gamestate
11
+
12
+ def initialize(log_level)
13
+ logger.level = log_level
14
+ logger.info 'Einfacher Spieler wurde erstellt.'
15
+ end
16
+
17
+ # gets called, when it's your turn
18
+ def move_requested
19
+ logger.info "Spielstand: #{gamestate.points_for_player(gamestate.current_player)} - #{gamestate.points_for_player(gamestate.other_player)}"
20
+ logger.debug "Board: #{gamestate.board}"
21
+ move = best_move
22
+ logger.debug "Zug gefunden: #{move}" unless move.nil?
23
+ move
24
+ end
25
+
26
+ def best_move
27
+ # gamestate.board.add_field(Field.new(5, 0))
28
+ logger.debug "Berechne zuege fuer Board #{gamestate.board}"
29
+
30
+ # all possible moves can't be calculated in under two seconds
31
+ possible_moves = GameRuleLogic.possible_moves(gamestate)
32
+ logger.debug "#{possible_moves.size} moegliche Zuege gefunden"
33
+ possible_moves.sample
34
+ end
35
+ end
data/example/main.rb CHANGED
@@ -1,42 +1,42 @@
1
- #!/usr/bin/env ruby
2
- # encoding: UTF-8
3
- # frozen_string_literal: true
4
- require 'software_challenge_client'
5
- require 'optparse'
6
- require 'ostruct'
7
-
8
- require_relative 'client'
9
-
10
- options = OpenStruct.new
11
- options.host = '127.0.0.1'
12
- options.port = 13_050
13
- options.reservation = ''
14
-
15
- opt_parser = OptionParser.new do |opt|
16
- opt.banner = 'Usage: main.rb [OPTIONS]'
17
- opt.separator ''
18
- opt.separator 'Options'
19
-
20
- opt.on('-p', '--port PORT', Integer, "connect to the server at PORT (default #{options.port})") do |p|
21
- options.port = p
22
- end
23
-
24
- opt.on('-h', '--host HOST', "the host's IP address (default #{options.host})") do |h|
25
- options.host = h
26
- end
27
-
28
- opt.on('-r', '--reservation RESERVATION', "the host's RESERVATION (default #{options.reservation})") do |r|
29
- options.reservation = r
30
- end
31
-
32
- opt.on_tail('-?', '--help', 'Show this message') do
33
- puts opt
34
- exit
35
- end
36
- end
37
-
38
- opt_parser.parse!(ARGV)
39
-
40
- client = Client.new(Logger::DEBUG)
41
- runner = Runner.new(options.host, options.port, client, options.reservation)
42
- runner.start
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+ # frozen_string_literal: true
4
+ require 'software_challenge_client'
5
+ require 'optparse'
6
+ require 'ostruct'
7
+
8
+ require_relative 'client'
9
+
10
+ options = OpenStruct.new
11
+ options.host = '127.0.0.1'
12
+ options.port = 13_050
13
+ options.reservation = ''
14
+
15
+ opt_parser = OptionParser.new do |opt|
16
+ opt.banner = 'Usage: main.rb [OPTIONS]'
17
+ opt.separator ''
18
+ opt.separator 'Options'
19
+
20
+ opt.on('-p', '--port PORT', Integer, "connect to the server at PORT (default #{options.port})") do |p|
21
+ options.port = p
22
+ end
23
+
24
+ opt.on('-h', '--host HOST', "the host's IP address (default #{options.host})") do |h|
25
+ options.host = h
26
+ end
27
+
28
+ opt.on('-r', '--reservation RESERVATION', "the host's RESERVATION (default #{options.reservation})") do |r|
29
+ options.reservation = r
30
+ end
31
+
32
+ opt.on_tail('-?', '--help', 'Show this message') do
33
+ puts opt
34
+ exit
35
+ end
36
+ end
37
+
38
+ opt_parser.parse!(ARGV)
39
+
40
+ client = Client.new(Logger::DEBUG)
41
+ runner = Runner.new(options.host, options.port, client, options.reservation)
42
+ runner.start
data/example/start.bat CHANGED
@@ -1,2 +1,2 @@
1
- :: Gedacht zum ausführen des ruby SimpleClients
2
- ruby main.rb %*
1
+ :: Gedacht zum ausführen des ruby SimpleClients
2
+ ruby main.rb %*
data/generate-authors.sh CHANGED
@@ -1,19 +1,19 @@
1
- #!/bin/bash
2
- # Thanks to the docker project!
3
- # https://github.com/docker/docker/blob/2c224e4fc09518d33780d818cf74026f6aa32744/hack/generate-authors.sh
4
- set -e
5
-
6
- # change to the directory where the script is located, this should be the project root
7
- pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")/"
8
-
9
- # see also ".mailmap" for how email addresses and names are deduplicated
10
-
11
- {
12
- cat <<-'EOH'
13
- # This file lists all individuals having contributed content to the repository.
14
- # For how it is generated, see `generate-authors.sh`.
15
- EOH
16
- echo
17
- git log --format='%aN <%aE>' | LC_ALL=C.UTF-8 sort -uf
18
- } > AUTHORS
19
- popd
1
+ #!/bin/bash
2
+ # Thanks to the docker project!
3
+ # https://github.com/docker/docker/blob/2c224e4fc09518d33780d818cf74026f6aa32744/hack/generate-authors.sh
4
+ set -e
5
+
6
+ # change to the directory where the script is located, this should be the project root
7
+ pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")/"
8
+
9
+ # see also ".mailmap" for how email addresses and names are deduplicated
10
+
11
+ {
12
+ cat <<-'EOH'
13
+ # This file lists all individuals having contributed content to the repository.
14
+ # For how it is generated, see `generate-authors.sh`.
15
+ EOH
16
+ echo
17
+ git log --format='%aN <%aE>' | LC_ALL=C.UTF-8 sort -uf
18
+ } > AUTHORS
19
+ popd
@@ -1,127 +1,149 @@
1
- # encoding: utf-8
2
- # frozen_string_literal: true
3
-
4
- require_relative './util/constants'
5
- require_relative 'game_state'
6
- require_relative 'field'
7
-
8
- # Ein Spielbrett fuer Ostseeschach
9
- class Board
10
- include Constants
11
-
12
- # @!attribute [r] fields
13
- # @note Besser über die {#field} Methode auf Felder zugreifen.
14
- # @return [Array<Array<Field>>] Ein Feld wird an der Position entsprechend
15
- # seiner x und y Coordinates im Array gespeichert.
16
- attr_reader :fields
17
-
18
- # Erstellt ein neues leeres Spielbrett.
19
- def initialize(fields = [])
20
- @fields = Board.empty_game_field
21
- fields.each { |f| add_field(f) }
22
- end
23
-
24
- # @return [Array] leere Felder entsprechend des Spielbrettes angeordnet
25
- def self.empty_game_field
26
- (0...BOARD_SIZE).to_a.map do |x|
27
- (0...BOARD_SIZE).to_a.map do |y|
28
- Field.new(x, y)
29
- end
30
- end
31
- end
32
-
33
- # Entfernt alle Felder des Spielfeldes
34
- def clear
35
- @fields = []
36
- end
37
-
38
- # @return [Array] Liste aller Felder
39
- def field_list
40
- @fields.flatten.reject(&:nil?)
41
- end
42
-
43
- # Vergleicht zwei Spielbretter. Gleichheit besteht, wenn zwei Spielbretter die
44
- # gleichen Felder enthalten.
45
- def ==(other)
46
- field_list == other.field_list
47
- end
48
-
49
- # Fügt ein Feld dem Spielbrett hinzu. Das übergebene Feld ersetzt das an den
50
- # Koordinaten bestehende Feld.
51
- #
52
- # @param field [Field] Das einzufügende Feld.
53
- def add_field(field)
54
- @fields[field.x][field.y] = field
55
- end
56
-
57
- # Zugriff auf die Felder des Spielfeldes
58
- #
59
- # @param x [Integer] Die X-Koordinate des Feldes.
60
- # @param y [Integer] Die Y-Koordinate des Feldes.
61
- # @return [Field] Das Feld mit den gegebenen Koordinaten. Falls das Feld nicht
62
- # exisitert, wird nil zurückgegeben.
63
- def field(x, y)
64
- fields.dig(x, y) # NOTE that #dig requires ruby 2.3+
65
- end
66
-
67
- # Zugriff auf die Felder des Spielfeldes über ein Koordinaten-Paar.
68
- #
69
- # @param coordinates [Coordinates] X- und Y-Koordinate als Paar, sonst wie
70
- # bei {Board#field}.
71
- #
72
- # @return [Field] Wie bei {Board#field}.
73
- #
74
- # @see #field
75
- def field_at(coordinates)
76
- field(coordinates.x, coordinates.y)
77
- end
78
-
79
- def fields_of_color(color)
80
- fields = []
81
-
82
- (0...BOARD_SIZE).to_a.map do |x|
83
- (0...BOARD_SIZE).to_a.map do |y|
84
- f = field(x,y)
85
- if (f.color == color)
86
- fields << f
87
- end
88
- end
89
- end
90
-
91
- fields
92
- end
93
-
94
- # @param it [Coordinates] Die zu untersuchenden Koordinaten
95
- # @return [Boolean] Ob die gegebenen Koordinaten auf dem Board liegen oder nicht
96
- def in_bounds?(it)
97
- it.x >= 0 && it.y >= 0 && it.x < BOARD_SIZE && it.y < BOARD_SIZE
98
- end
99
-
100
- # @return eine unabhaengige Kopie des Spielbretts
101
- def clone
102
- Marshal.load(Marshal.dump(self))
103
- end
104
-
105
- # @param coords [Coordinates] Die Koordinaten des Felds
106
- # @return Das Feld an den gegebenen Koordinaten
107
- def [](coords)
108
- field_at(coords)
109
- end
110
-
111
- # Gibt eine textuelle Repräsentation des Spielbrettes aus.
112
- def to_s
113
- "\n" +
114
- (0...BOARD_SIZE).to_a.map do |y|
115
- (0...BOARD_SIZE).to_a.map do |x|
116
- @fields[x][y].to_s
117
- end.join(' ')
118
- end.join("\n")
119
- end
120
-
121
- # @param position [Coordinates] Die zu überprüfenden Koordinaten
122
- # @return Ob die gegebenen Koordinaten auf dem board liegen
123
- def self.contains(position)
124
- position.x >= 0 && position.x < BOARD_SIZE &&
125
- position.y >= 0 && position.y < BOARD_SIZE
126
- end
127
- end
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require_relative './util/constants'
5
+ require_relative 'game_state'
6
+ require_relative 'field'
7
+
8
+ # Ein Spielbrett fuer Ostseeschach
9
+ class Board
10
+ include Constants
11
+
12
+ # @!attribute [r] fields
13
+ # @note Besser über die {#field} Methode auf Felder zugreifen.
14
+ # @return [Array<Array<Field>>] Ein Feld wird an der Position entsprechend
15
+ # seiner x und y Coordinates im Array gespeichert.
16
+ attr_reader :fields
17
+
18
+ # --- Init ------------------------------------------------------------
19
+
20
+ # Erstellt ein neues leeres Spielbrett.
21
+ def initialize(fields = [])
22
+ @fields = Board.empty_game_field
23
+ fields.each { |f| add_field(f) }
24
+ end
25
+
26
+ # @return [Array] leere Felder entsprechend des Spielbrettes angeordnet
27
+ def self.empty_game_field
28
+ (0...BOARD_SIZE).to_a.map do |x|
29
+ (0...BOARD_SIZE).to_a.map do |y|
30
+ Field.new(x, y)
31
+ end
32
+ end
33
+ end
34
+
35
+ # Entfernt alle Felder des Spielfeldes
36
+ def clear
37
+ @fields = []
38
+ end
39
+
40
+ # --- Field Access ------------------------------------------------------------
41
+
42
+ # Fügt ein Feld dem Spielbrett hinzu. Das übergebene Feld ersetzt das an den
43
+ # Koordinaten bestehende Feld.
44
+ #
45
+ # @param field [Field] Das einzufügende Feld.
46
+ def add_field(field)
47
+ @fields[field.x][field.y] = field
48
+ end
49
+
50
+ # Zugriff auf die Felder des Spielfeldes
51
+ #
52
+ # @param x [Integer] Die X-Koordinate des Feldes.
53
+ # @param y [Integer] Die Y-Koordinate des Feldes.
54
+ # @return [Field] Das Feld mit den gegebenen Koordinaten. Falls das Feld nicht
55
+ # exisitert, wird nil zurückgegeben.
56
+ def field(x, y)
57
+ fields.dig(x, y) # NOTE that #dig requires ruby 2.3+
58
+ end
59
+
60
+ # Zugriff auf die Felder des Spielfeldes über ein Koordinaten-Paar.
61
+ #
62
+ # @param coords [Coordinates] X- und Y-Koordinate als Paar, sonst wie
63
+ # bei {Board#field}.
64
+ #
65
+ # @return [Field] Wie bei {Board#field}.
66
+ #
67
+ # @see #field
68
+ def field_at(coords)
69
+ field(coords.x, coords.y)
70
+ end
71
+
72
+ # @return [Array] Liste aller Felder
73
+ def field_list
74
+ @fields.flatten.reject(&:nil?)
75
+ end
76
+
77
+ # @param coords [Coordinates] Die Koordinaten des Felds
78
+ # @return Das Feld an den gegebenen Koordinaten
79
+ def [](coords)
80
+ field_at(coords)
81
+ end
82
+
83
+ def fields_of_team(team)
84
+ fields = []
85
+
86
+ (0...BOARD_SIZE).to_a.map do |x|
87
+ (0...BOARD_SIZE).to_a.map do |y|
88
+ f = field(x,y)
89
+ if (!f.piece.nil? && f.piece.team == team)
90
+ fields << f
91
+ end
92
+ end
93
+ end
94
+
95
+ fields
96
+ end
97
+
98
+ # @param field [Field] Das eingabe Feld
99
+ # @return Die Felder um dem gegebenen Feld
100
+ def neighbors_of(field)
101
+ coords = []
102
+ c = Coordinates.oddr_to_doubled(field.coords)
103
+
104
+ Direction.each { |d|
105
+ disp = d.to_vec()
106
+
107
+ x = c.x + disp.x
108
+ y = c.y + disp.y
109
+
110
+ oddr_coords = Coordinates.doubled_to_oddr_int(x, y)
111
+ if !in_bounds?(oddr_coords)
112
+ next
113
+ end
114
+
115
+ coords.push(oddr_coords)
116
+ }
117
+
118
+ coords.map{ |x| self.field_at(x) }.to_a
119
+ end
120
+
121
+ # --- Other ------------------------------------------------------------
122
+
123
+ # @param coords [Coordinates] Die zu untersuchenden Koordinaten
124
+ # @return [Boolean] Ob die gegebenen Koordinaten auf dem Board liegen oder nicht
125
+ def in_bounds?(coords)
126
+ coords.x >= 0 && coords.y >= 0 && coords.x < BOARD_SIZE && coords.y < BOARD_SIZE
127
+ end
128
+
129
+ # Vergleicht zwei Spielbretter. Gleichheit besteht, wenn zwei Spielbretter die
130
+ # gleichen Felder enthalten.
131
+ def ==(other)
132
+ field_list == other.field_list
133
+ end
134
+
135
+ # @return eine unabhaengige Kopie des Spielbretts
136
+ def clone
137
+ Marshal.load(Marshal.dump(self))
138
+ end
139
+
140
+ # Gibt eine textuelle Repräsentation des Spielbrettes aus.
141
+ def to_s
142
+ "\n" +
143
+ (0...BOARD_SIZE).to_a.map do |y|
144
+ (0...BOARD_SIZE).to_a.map do |x|
145
+ @fields[x][y].to_s
146
+ end.join(' ')
147
+ end.join("\n")
148
+ end
149
+ end
@@ -1,19 +1,19 @@
1
- # encoding: utf-8
2
- # frozen_string_literal: true
3
-
4
- # Das Interface sollte von einem Client implementiert werden, damit er über das
5
- # Gem an einem Spiel teilnehmen kann.
6
- class ClientInterface
7
- # Wird automatisch aktualisiert und ist immer der Spielzustand des aktuellen Zuges.
8
- attr_accessor :gamestate
9
-
10
- # Wird aufgerufen, wenn der Client einen Zug machen soll. Dies ist der
11
- # Einstiegspunkt für die eigentliche Logik des Computerspielers. Er muss auf
12
- # Basis des Spielzustandes entscheiden, welchen Zug er machen möchte und diese
13
- # zurückgeben.
14
- #
15
- # @return [Move] Ein für den aktuellen Spielzustand gültiger Spielzug.
16
- def move_requested
17
- raise 'Not yet implemented'
18
- end
19
- end
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ # Das Interface sollte von einem Client implementiert werden, damit er über das
5
+ # Gem an einem Spiel teilnehmen kann.
6
+ class ClientInterface
7
+ # Wird automatisch aktualisiert und ist immer der Spielzustand des aktuellen Zuges.
8
+ attr_accessor :gamestate
9
+
10
+ # Wird aufgerufen, wenn der Client einen Zug machen soll. Dies ist der
11
+ # Einstiegspunkt für die eigentliche Logik des Computerspielers. Er muss auf
12
+ # Basis des Spielzustandes entscheiden, welchen Zug er machen möchte und diese
13
+ # zurückgeben.
14
+ #
15
+ # @return [Move] Ein für den aktuellen Spielzustand gültiger Spielzug.
16
+ def move_requested
17
+ raise 'Not yet implemented'
18
+ end
19
+ end
@@ -1,27 +1,27 @@
1
- # encoding: UTF-8
2
- # frozen_string_literal: true
3
- require_relative 'player'
4
-
5
- # Das Ergebnis eines Spieles. Ist im `GameState#condition` zu finden, wenn das Spiel beendet wurde.
6
- class Condition
7
- # @!attribute [r] winner
8
- # @return [Player] Spieler, der das Spiel gewonnen hat.
9
- attr_reader :winner
10
-
11
- # @!attribute [r] reason
12
- # @return [String] Grund fuer Spielende
13
- attr_reader :reason
14
-
15
- # Initializes the winning Condition with a player
16
- # @param winner [Player] winning player
17
- # @param reason [String] why the player has won
18
- def initialize(winner, reason)
19
- @winner = winner
20
- @reason = reason
21
- end
22
-
23
- # Überprüfe ob es ein Unentschieden gab
24
- def draw?
25
- @winner.nil?
26
- end
27
- end
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+ require_relative 'player'
4
+
5
+ # Das Ergebnis eines Spieles. Ist im `GameState#condition` zu finden, wenn das Spiel beendet wurde.
6
+ class Condition
7
+ # @!attribute [r] winner
8
+ # @return [Player] Spieler, der das Spiel gewonnen hat.
9
+ attr_reader :winner
10
+
11
+ # @!attribute [r] reason
12
+ # @return [String] Grund fuer Spielende
13
+ attr_reader :reason
14
+
15
+ # Initializes the winning Condition with a player
16
+ # @param winner [Player] winning player
17
+ # @param reason [String] why the player has won
18
+ def initialize(winner, reason)
19
+ @winner = winner
20
+ @reason = reason
21
+ end
22
+
23
+ # Überprüfe ob es ein Unentschieden gab
24
+ def draw?
25
+ @winner.nil?
26
+ end
27
+ end