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

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


101
102
103
# File 'lib/gameworks/game.rb', line 101

def active_players
  raise NotImplementedError
end

#add_move(payload, player) ⇒ Object



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

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
  legal, error = process_move(move, player)
  return false, error unless legal
  ignore_disqualified_players
  update_observers
  return true
end

#add_player(payload = {}) ⇒ Object



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

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



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

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

#build_move(payload, player) ⇒ Object

Raises:

  • (NotImplementedError)


139
140
141
# File 'lib/gameworks/game.rb', line 139

def build_move(payload, player)
  raise NotImplementedError
end

#delta(snapshot, for_player = nil) ⇒ Object



60
61
62
63
# File 'lib/gameworks/game.rb', line 60

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

#disqualify(player) ⇒ Object



151
152
153
154
155
156
157
158
159
160
# File 'lib/gameworks/game.rb', line 151

def disqualify(player)
  player.disqualify!
  update_observers
  if @players.select { |p| !p.disqualified? }.size < 2
    end_game
  else
    update_observers
    end_turn
  end
end

#end_gameObject



162
163
164
165
166
167
# File 'lib/gameworks/game.rb', line 162

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)


65
66
67
# File 'lib/gameworks/game.rb', line 65

def full?
  raise NotImplementedError
end

#generate_uuidObject



97
98
99
# File 'lib/gameworks/game.rb', line 97

def generate_uuid
  SecureRandom.uuid
end

#ignore_disqualified_playersObject



105
106
107
# File 'lib/gameworks/game.rb', line 105

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

#init_gameObject



92
93
94
95
# File 'lib/gameworks/game.rb', line 92

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

#initial_snapshotObject



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

def initial_snapshot
  @initial_snapshot ||= snapshot
end

#move_legal?(move, player) ⇒ Boolean

Returns:

  • (Boolean)

Raises:

  • (NotImplementedError)


143
144
145
# File 'lib/gameworks/game.rb', line 143

def move_legal?(move, player)
  raise NotImplementedError
end

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

Returns:

  • (Boolean)


56
57
58
# File 'lib/gameworks/game.rb', line 56

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

#player_classObject



69
70
71
# File 'lib/gameworks/game.rb', line 69

def player_class
  Gameworks::Player
end

#player_for_token(token) ⇒ Object



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

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

#process_move(move, player) ⇒ Object

Raises:

  • (NotImplementedError)


147
148
149
# File 'lib/gameworks/game.rb', line 147

def process_move(move, player)
  raise NotImplementedError
end

#register_observer(cb) ⇒ Object



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

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

#signal_turnsObject



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

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



173
174
175
# File 'lib/gameworks/game.rb', line 173

def snapshot
  snapshot_class.new(self)
end

#snapshot_classObject



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

def snapshot_class
  Gameworks::Game::Snapshot
end

#snapshot_for_observer(token) ⇒ Object



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

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



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

def start_if_ready
  if full?
    init_game
    signal_turns
    update_observers
  end
end

#to_json(for_player = nil) ⇒ Object



182
183
184
# File 'lib/gameworks/game.rb', line 182

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

#update_observers(end_game = false) ⇒ Object



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

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, delta(initial_snapshot).to_json)
end