Class: Gameworks::Game

Inherits:
Object
  • Object
show all
Defined in:
lib/gameworks/game.rb,
lib/gameworks/game_snapshot.rb

Defined Under Namespace

Classes: Snapshot

Constant Summary collapse

TURN_TIME_LIMIT =
30
DEFAULT_DELAY =
0.3
STATE_INITIATING =
'initiating'
STATE_IN_PLAY =
'in play'
STATE_COMPLETED =
'completed'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(payload = {}) ⇒ Game

Returns a new instance of Game.


17
18
19
20
21
22
23
24
25
26
27
# File 'lib/gameworks/game.rb', line 17

def initialize(payload={})
  @notifier = Notifier.new
  @players = []
  @state = STATE_INITIATING
  @delay_time = payload['delay_time'] || DEFAULT_DELAY
  @turns = {}
  @observers = {}
  @fuses = {}
  @id = generate_uuid.split('-').first
  @moves = {}
end

Instance Attribute Details

#delay_timeObject (readonly)

Returns the value of attribute delay_time


15
16
17
# File 'lib/gameworks/game.rb', line 15

def delay_time
  @delay_time
end

#idObject (readonly)

Returns the value of attribute id


15
16
17
# File 'lib/gameworks/game.rb', line 15

def id
  @id
end

#movesObject (readonly)

Returns the value of attribute moves


15
16
17
# File 'lib/gameworks/game.rb', line 15

def moves
  @moves
end

#notifierObject (readonly)

Returns the value of attribute notifier


15
16
17
# File 'lib/gameworks/game.rb', line 15

def notifier
  @notifier
end

#playersObject (readonly)

Returns the value of attribute players


15
16
17
# File 'lib/gameworks/game.rb', line 15

def players
  @players
end

#stateObject (readonly)

Returns the value of attribute state


15
16
17
# File 'lib/gameworks/game.rb', line 15

def state
  @state
end

Instance Method Details

#active_playersObject

Raises:

  • (NotImplementedError)

99
100
101
# File 'lib/gameworks/game.rb', line 99

def active_players
  raise NotImplementedError
end

#add_move(payload, player) ⇒ Object


124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/gameworks/game.rb', line 124

def add_move(payload, player)
  @fuses[player].abort

  move, error = build_move(payload, player)
  return false, error unless move

  legal, error = move_legal?(move, player)
  return false, error unless legal

  moves[player] = move
  process_move(move, player)

  ignore_disqualified_players
  update_observers
  true
end

#add_player(payload = {}) ⇒ Object


79
80
81
82
83
84
85
86
87
88
# File 'lib/gameworks/game.rb', line 79

def add_player(payload={})
  return false if full?

  player = player_class.new(payload)
  return false unless player.valid?

  @players << player
  update_observers
  player
end

#as_json(for_player = nil) ⇒ Object


183
184
185
186
187
# File 'lib/gameworks/game.rb', line 183

def as_json(for_player=nil)
  { :id      => id,
    :players => @players.map{ |player| player.as_json(for_player) },
    :state   => @state }
end

#build_move(payload, player) ⇒ Object

Raises:

  • (NotImplementedError)

141
142
143
# File 'lib/gameworks/game.rb', line 141

def build_move(payload, player)
  raise NotImplementedError
end

#delta(snapshot, for_player = nil) ⇒ Object


57
58
59
60
61
# File 'lib/gameworks/game.rb', line 57

def delta(snapshot, for_player=nil)
  { :id => id,
    :players => @players.select{ |p| player_changed?(p, snapshot, for_player) }.map{ |p| p.as_json(for_player) },
    :state   => @state }
end

#disqualify(player) ⇒ Object


157
158
159
160
161
162
163
164
165
166
# File 'lib/gameworks/game.rb', line 157

def disqualify(player)
  player.disqualify!
  update_observers
  if @players.select { |p| !p.disqualified? }.size < 2
    end_game
  elsif !waiting_on_moves?
    signal_turns
  end
  # else do nothing; waiting for other players
end

#end_gameObject


168
169
170
171
172
173
# File 'lib/gameworks/game.rb', line 168

def end_game
  @state = STATE_COMPLETED
  @fuses.values.each(&:abort)
  update_observers(true)
  @players.each{ |p| p.signal_turn(nil) }
end

#full?Boolean

Returns:

  • (Boolean)

Raises:

  • (NotImplementedError)

63
64
65
# File 'lib/gameworks/game.rb', line 63

def full?
  raise NotImplementedError
end

#generate_uuidObject


95
96
97
# File 'lib/gameworks/game.rb', line 95

def generate_uuid
  SecureRandom.uuid
end

#ignore_disqualified_playersObject


103
104
105
# File 'lib/gameworks/game.rb', line 103

def ignore_disqualified_players
  @players.rotate! while @players.first.disqualified?
end

#init_gameObject


90
91
92
93
# File 'lib/gameworks/game.rb', line 90

def init_game
  @players.shuffle!
  @state = STATE_IN_PLAY
end

#move_legal?(move, player) ⇒ Boolean

Returns:

  • (Boolean)

Raises:

  • (NotImplementedError)

145
146
147
# File 'lib/gameworks/game.rb', line 145

def move_legal?(move, player)
  raise NotImplementedError
end

#player_changed?(player, snapshot, for_player = nil) ⇒ Boolean

Returns:

  • (Boolean)

53
54
55
# File 'lib/gameworks/game.rb', line 53

def player_changed?(player, snapshot, for_player=nil)
  player.score != snapshot.player_scores[player]
end

#player_classObject


67
68
69
# File 'lib/gameworks/game.rb', line 67

def player_class
  Gameworks::Player
end

#player_for_token(token) ⇒ Object


120
121
122
# File 'lib/gameworks/game.rb', line 120

def player_for_token(token)
  @turns.delete(token)
end

#process_move(move, player) ⇒ Object

Raises:

  • (NotImplementedError)

149
150
151
# File 'lib/gameworks/game.rb', line 149

def process_move(move, player)
  raise NotImplementedError
end

#register_observer(cb) ⇒ Object


29
30
31
32
33
# File 'lib/gameworks/game.rb', line 29

def register_observer(cb)
  token = generate_uuid
  @observers[token] = [self.snapshot, cb]
  token
end

#signal_turnsObject


107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/gameworks/game.rb', line 107

def signal_turns
  moves.clear
  active_players.each do |player|
    token = generate_uuid
    @turns[token] = player
    # This may no longer be necessary, since we abort the players' fuses
    # when they submit turns in #add_move.
    @fuses[player].abort if @fuses[player]
    @fuses[player] = Gameworks::Fuse.new(TURN_TIME_LIMIT) { disqualify(player) }
    player.signal_turn(token)
  end
end

#snapshotObject


179
180
181
# File 'lib/gameworks/game.rb', line 179

def snapshot
  snapshot_class.new(self)
end

#snapshot_classObject


175
176
177
# File 'lib/gameworks/game.rb', line 175

def snapshot_class
  Gameworks::Game::Snapshot
end

#snapshot_for_observer(token) ⇒ Object


44
45
46
47
48
49
50
51
# File 'lib/gameworks/game.rb', line 44

def snapshot_for_observer(token)
  snapshot = nil
  if @observers.has_key?(token)
    snapshot, cb = @observers[token]
    @observers[token][0] = self.snapshot
  end
  snapshot
end

#start_if_readyObject


71
72
73
74
75
76
77
# File 'lib/gameworks/game.rb', line 71

def start_if_ready
  if full?
    init_game
    signal_turns
    update_observers
  end
end

#to_json(for_player = nil) ⇒ Object


189
190
191
# File 'lib/gameworks/game.rb', line 189

def to_json(for_player=nil)
  as_json(for_player).to_json
end

#update_observers(end_game = false) ⇒ Object


35
36
37
38
39
40
41
42
# File 'lib/gameworks/game.rb', line 35

def update_observers(end_game = false)
  @observers.each do |token, (_, cb)|
    snapshot = snapshot_for_observer(token)
    cb.call(delta(snapshot).to_json)
    cb.succeed if end_game
  end
  notifier.push(self, to_json)
end

#waiting_on_moves?Boolean

Returns:

  • (Boolean)

153
154
155
# File 'lib/gameworks/game.rb', line 153

def waiting_on_moves?
  (active_players - moves.keys).any?
end