Skip to content

Commit

Permalink
feat: grid game playable
Browse files Browse the repository at this point in the history
  • Loading branch information
abradner committed Jul 11, 2024
1 parent 30d4021 commit 6db665d
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 16 deletions.
39 changes: 39 additions & 0 deletions app/controllers/grid_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,43 @@ def show
grid = Grid.new(seed: seed, size: size)
render json: { seed: seed, size: size, solution: true, grid: grid.as_json! }
end

def update
seed = params.require(:id).to_i
size = params.require(:size).to_i

proposals = params.require(:proposals)
raise "Expected proposals to be an array" unless proposals.is_a?(Array)

proposals_count = proposals.count
expected_count = Grid::SUB_GRIDS.values.sum
raise "need the right number of proposals (#{proposals_count} of #{expected_count}" if proposals_count != expected_count

grid = Grid.new(seed: seed, size: size)

errors = []
proposals.each do |proposal|
top_left = proposal[:top_left]
if top_left.nil? ||
!top_left.is_a?(Array) ||
top_left.size != 2 ||
!top_left.all? { |el| el.is_a?(Integer) }
raise "Expected top_left (#{top_left}) to be an array of int len(2)"
end
size = proposal[:size]
if size.nil? || !size.is_a?(Integer)
raise "Expected size (#{size}) to be an integer"
end

grid.add_to_proposed_solution!(size, top_left)

rescue ArgumentError => e
errors << [proposal, e.message]
end

payload = { seed: seed, size: size, proposed_solution: grid.proposed_as_json, solved: grid.solved?, grid: grid }
payload[:errors] = errors if errors.any?

render json: payload
end
end
8 changes: 4 additions & 4 deletions app/data/arg.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

Arg = Data.define(:name, :type) do
Arg = Data.define(:name, :type, :required, :default) do
Boolean = ActiveModel::Type::Boolean # local alias, making underlying datastructures less AM-dependent
WHITELIST = %w[String Integer Float Hash] << Boolean.to_s
MAPPABLE_WHITELIST = {
Expand All @@ -11,7 +11,7 @@
object: "Hash",
}.stringify_keys

def initialize(name:, type:)
def initialize(name:, type:, required:, default: nil)
t_class =
if %w[String Symbol].include? type.class.to_s
t_sanitised = MAPPABLE_WHITELIST.include?(type) ? MAPPABLE_WHITELIST[type] : type
Expand All @@ -24,14 +24,14 @@ def initialize(name:, type:)
raise ArgumentError, "Unexpected type #{type}"
end

super(name: name, type: t_class)
super(name: name, type: t_class, required: required, default: default)
end

def type_as_json
MAPPABLE_WHITELIST.key(type.to_s)
end

def as_json
{ name: name, type: type_as_json }.stringify_keys
{ name: name, type: type_as_json, required: required, default: default }.stringify_keys
end
end
4 changes: 2 additions & 2 deletions app/models/game.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ class Game
name: "Forest Grid",
klass: "Grid",
args: [
{ name: "seed", type: "int" },
{ name: "size", type: "int" },
{ name: "seed", type: "int", required: true, default: nil },
{ name: "size", type: "int", required: false, default: Grid::DEFAULT_SIZE },
],
},
].map { |game| StaticGame.new(**game) }
Expand Down
44 changes: 34 additions & 10 deletions app/services/grid.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,22 @@ class Grid
}.freeze
DEFAULT_SIZE = 5.freeze

CELL_CAPACITY = 4.freeze

def initialize(seed:, size: DEFAULT_SIZE)
@rng = Random.new(seed)
@size = size
@matrix = Matrix.build(@size) { 0 }
@proposed = Matrix.build(@size) { 0 }
@construction = []

build_solution
end

def add_to_proposal!(size, top_left)
def add_to_proposed_solution!(size, top_left)
test = @proposed + place_matrix(size, top_left)
if (@matrix - test).any? { |el| el < 0 }
raise ArgumentError, "bad solution"
raise ArgumentError, "bad solution".freeze
end
@proposed = test
end
Expand All @@ -55,6 +58,14 @@ def matrix
@matrix
end

def construction
@construction
end

def solved?
@matrix == @proposed
end

def show
puts to_s(hide_vals: true)
end
Expand All @@ -63,12 +74,18 @@ def show!
puts to_s(hide_vals: false)
end

def as_json(hide_vals: true)
{
def proposed_as_json
matrix_as_json(@proposed, hide_vals: false)
end

def as_json(hide_vals: true, matrix: @matrix)
h = {
seed: @rng.seed,
size: @size,
matrix: matrix_as_json(@matrix, hide_vals: hide_vals),
matrix: matrix_as_json(matrix, hide_vals: hide_vals),
}
h[:construction] = @construction unless hide_vals
h
end

def as_json!
Expand Down Expand Up @@ -96,21 +113,28 @@ def to_s(hide_vals: false)
private

def build_solution
SUB_GRIDS.each do |size, count|
# For each component type, starting from the largest
SUB_GRIDS.sort_by { |k, _v| k }.reverse.each do |size, count|
furthest_position = @size - size + 1
count.times do
top_left = [ @rng.rand(furthest_position), @rng.rand(furthest_position) ]
top_left = [@rng.rand(furthest_position), @rng.rand(furthest_position)]
add_to_solution!(size, top_left)
rescue RangeError
puts "seed #{@rng.seed} failed to place a #{size}x#{size} component at #{top_left}, retrying..."
retry
end
end
end

def add_to_solution!(size, top_left)
@matrix += place_matrix(size, top_left)
test = @matrix + place_matrix(size, top_left)
if test.any? { |el| el > CELL_CAPACITY }
raise RangeError, "placement is out of bounds".freeze
end
@matrix = test
@construction << { size: size, top_left: top_left }
end



def matrix_as_json(matrix, hide_vals: false)
local_rng = Random.new(@rng.seed)
matrix.row_vectors.map do |row|
Expand Down

0 comments on commit 6db665d

Please sign in to comment.