Was this helpful?
class Game
include Hyperstack::State::Observable def initialize
@history = [[]]
@step = 0
end observer :player do
@step.even? ? :X : :O
end
observer :current do
@history[@step]
end attr_reader :history WINNING_COMBOS = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6]]
def current_winner?
WINNING_COMBOS.each do |a, b, c|
return current[a] if current[a] && current[a] == current[b] && current[a] == current[c]
end
false
end mutator :handle_click! do |id|
board = history[@step]
return if current_winner? || board[id]
board = board.dup
board[id] = player
@history = history[0..@step] + [board]
@step += 1
end
mutator(:jump_to!) { |step| @step = step }
endclass DisplayGame < HyperComponent
before_mount { @game = Game.new }
def moves
return unless @game.history.length > 1
@game.history.length.times do |move|
LI(key: move) { move.zero? ? "Go to game start" : "Go to move ##{move}" }
.on(:click) { @game.jump_to!(move) }
end
end
def status
if (winner = @game.current_winner?)
"Winner: #{winner}"
else
"Next player: #{@game.player}"
end
end
render(DIV, class: :game) do
DIV(class: :game_board) do
DisplayCurrentBoard(game: @game)
end
DIV(class: :game_info) do
DIV { status }
OL { moves }
end
end
endclass DisplayCurrentBoard < HyperComponent
param :game
def draw_square(id)
BUTTON(class: :square, id: id) { game.current[id] }
.on(:click) { game.handle_click!(id) }
end
render(DIV) do
(0..6).step(3) do |row|
DIV(class: :board_row) do
(row..row + 2).each { |id| draw_square(id) }
end
end
end
endclass Game
include Hyperstack::State::Observable
class << self
def initialize
@history = [[]]
@step = 0
end
observer :player do
@step.even? ? :X : :O
end
observer :current do
@history[@step]
end
state_reader :history
WINNING_COMBOS = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6]]
def current_winner?
WINNING_COMBOS.each do |a, b, c|
return current[a] if current[a] && current[a] == current[b] && current[a] == current[c]
end
false
end
mutator :handle_click! do |id|
board = history[@step]
return if current_winner? || board[id]
board = board.dup
board[id] = player
@history = history[0..@step] + [board]
@step += 1
end
mutator(:jump_to!) { |step| @step = step }
end
end
class DisplayBoard < HyperComponent
param :board
def draw_square(id)
BUTTON(class: :square, id: id) { board[id] }
.on(:click) { Game.handle_click!(id) }
end
render(DIV) do
(0..6).step(3) do |row|
DIV(class: :board_row) do
(row..row + 2).each { |id| draw_square(id) }
end
end
end
end
class DisplayGame < HyperComponent
def moves
return unless Game.history.length > 1
Game.history.length.times do |move|
LI(key: move) { move.zero? ? "Go to game start" : "Go to move ##{move}" }
.on(:click) { Game.jump_to!(move) }
end
end
def status
if (winner = Game.current_winner?)
"Winner: #{winner}"
else
"Next player: #{Game.player}"
end
end
render(DIV, class: :game) do
DIV(class: :game_board) do
DisplayBoard(board: Game.current)
end
DIV(class: :game_info) do
DIV { status }
OL { moves }
end
end
endclass DisplaySquare
param :id
render
BUTTON(class: :square, id: id) { Game.current[id] }
.on(:click) { Game.handle_click(id) }
end
end
class DisplayBoard < HyperComponent
render(DIV) do
(0..6).step(3) do |row|
DIV(class: :board_row) do
(row..row + 2).each { |id| DisplaySquare(id: id) }
end
end
end
end# evaluate and return a value
observe @history[@step]
# evaluate a block and return its value
observe do
@history[@step]
endobserver :foo do |x, y, z|
...
enddef foo(x, y, z)
observe do
...
end
endstate_reader :bar, :bazdef bar
observe @bar
end
def baz
observe @baz
end# evaluate and return a value
mutate @history[@step]
# evaluate a block and return its value
mutate do
@history[@step]
endmutator :foo do |x, y, z|
...
enddef foo(x, y, z)
mutate do
...
end
endstate_reader :bar, :bazdef bar=(x)
mutate @bar = x
end
def baz=(x)
observe @baz = x
endtoggle(:foo)mutate @foo = !@foostate_accessor :foo, :barstate_reader :foo, :bar
state_writer :foo, :bar