Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions katas/2-generic_falling_blocks_game_solver/eddbot/Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require "minitest/test_task"

Minitest::TestTask.create(:test) do |t|
t.test_globs = ["test/**/*_test.rb"]
end

task :default => [:test]
11 changes: 11 additions & 0 deletions katas/2-generic_falling_blocks_game_solver/eddbot/main.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# This is an empty main file for your kata
# You can run it with `bundle exec ruby katas/generic_falling_blocks_game_solver/eddsansome/main.rb`
# Feel free to add your code below and remove this comment ...

require 'bundler/inline'

gemfile do
source 'https://rubygems.org'

# Add your gems here
end
192 changes: 192 additions & 0 deletions katas/2-generic_falling_blocks_game_solver/eddbot/src/solver.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
# frozen_string_literal: true

class Solver
def initialize(game_plane)
@game_plane = game_plane
end

# bounds checking
def get_val(row_val, col_val)
return 1 if row_val >= game_board.size || col_val >= game_board.first.size || row_val.negative? || col_val.negative?

game_board[row_val][col_val]
end

def recommended_block
# brute force

# for each 0 square on the gameboard, from bottom to top
# superimpose each piece over the board, and add one to each
# grid num

# if all of the nums are 1, then the shape fits
# otherwise the shape doens't

# we need a way to check which was found first, and simply return it

5.downto(0).to_a.each do |row|
next if game_board[row].sum == 10 # skip if row is filled

if game_board[row].sum.zero? && row <= 1 # lob any old shit in if the rows are clear
self.orientation = :any

return 'Any'
end

recs = []
0.upto(9).to_a.each do |col|
# if the block is filled, just skip
next if game_board[row][col] == 1

square = get_val(row, col) + get_val(row, col + 1) + get_val(row - 1, col) + get_val(row - 1, col + 1)

if square.zero?
score = []
# how are we going to check the score?
score << 10 if game_board[row].sum == 8
score << 10 if game_board[row - 1].sum == 8

recs << { shape: 'Square', score: score.sum, orientation: :any }
end

upwards_line = get_val(row, col) + get_val(row - 1, col) + get_val(row - 2, col) + get_val(row - 3, col)

if upwards_line.zero?

score = []

score << 10 if game_board[row].sum == 9
score << 10 if game_board[row - 1].sum == 9
score << 10 if game_board[row - 2].sum == 9
score << 10 if game_board[row - 3].sum == 9

recs << { shape: 'Line', score: score.sum, orientation: :orientation_1 }
end

sideways_line = get_val(row, col) + get_val(row, col + 1) + get_val(row, col + 2) + get_val(row, col + 3)

if sideways_line.zero?
score = 0

score = 10 if game_board[row].sum == 6

recs << { shape: 'Line', score: score, orientation: :orientation_2 }
end

z_block_sideways = get_val(row, col) + get_val(row, col + 1) + get_val(row - 1, col - 1) + get_val(row - 1, col)

if z_block_sideways.zero?
score = []

score << 10 if game_board[row].sum == 8
# this should really be 8, but otherwise it will be equal with the square.
score << 10 if game_board[row - 1].sum == 7

recs << { shape: 'Z', score: score.sum, orientation: :orientation_1 }
end

z_block_upwards = get_val(row,
col) + get_val(row - 1, col) + get_val(row - 1, col + 1) + get_val(row - 2, col + 1)

if z_block_upwards.zero?
score = []

score << 10 if game_board[row].sum == 9
score << 10 if game_board[row - 1].sum == 8
score << 10 if game_board[row - 2].sum == 9

recs << { shape: 'Z', score: score.sum, orientation: :orientation_2 }
end

l_block_1 = get_val(row, col) + get_val(row - 1, col) + get_val(row - 2, col) + get_val(row - 2, col - 1)
if l_block_1.zero?
score = []

score << 10 if game_board[row].sum == 9
score << 10 if game_board[row - 1].sum == 9
score << 10 if game_board[row - 2].sum == 8

recs << { shape: 'L', score: score.sum, orientation: :orientation_3 }
end

l_block_2 = get_val(row, col) + get_val(row - 1, col) + get_val(row - 1, col + 1) + get_val(row - 1, col + 2)
if l_block_2.zero?
score = []

score << 10 if game_board[row].sum == 9
score << 10 if game_board[row - 1].sum == 7

recs << { shape: 'L', score: score.sum, orientation: :orientation_2 }
end

l_block_3 = get_val(row, col) + get_val(row, col + 1) + get_val(row, col + 2) + get_val(row - 1, col + 2)

if l_block_3.zero?
score = []

score << 10 if game_board[row].sum == 7
score << 10 if game_board[row - 1].sum == 9

recs << { shape: 'L', score: score.sum, orientation: :orientation_4 }
end

n_block_sideways = get_val(row,
col) + get_val(row, col + 1) + get_val(row - 1, col + 1) + get_val(row - 1, col + 2)

if n_block_sideways.zero?
score = []

score << 10 if game_board[row].sum == 8
# this should really be 8, but otherwise it will be equal with the square.
score << 10 if game_board[row - 1].sum == 7

recs << { shape: 'N', score: score.sum, orientation: :orientation_1 }
end

n_block_upwards = get_val(row,
col) + get_val(row - 1, col) + get_val(row - 1, col - 1) + get_val(row - 2, col - 1)

if n_block_upwards.zero?
score = []

score << 10 if game_board[row].sum == 9
score << 10 if game_board[row - 1].sum == 8
score << 10 if game_board[row - 2].sum == 9

recs << { shape: 'N', score: score.sum, orientation: :orientation_2 }
end

# for each row find the max score and return it
next unless recs.any?

winner = recs.max { |a, b| a[:score] <=> b[:score] }

self.orientation = winner[:orientation]

return winner[:shape]
end
end
end

def recommended_orientation
recommended_block

orientation
end

private

attr_reader :game_plane

attr_accessor :orientation

def game_board
@game_board ||= game_plane.split("\n").map do |line|
if line == ''
Array.new(10, 0)
else
line.chars.each_slice(2).map { _1.join == '▓▓' ? 1 : 0 }
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
require 'minitest/autorun'
require_relative '../../src/solver'

class GenericFallingBlocksGameIntegrationTest < Minitest::Test
def test_recommends_any_block_when_plane_is_level
game_plane = <<~PLANE


▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
PLANE
assert_equal 'Any', described_class.new(game_plane).recommended_block
assert_equal :any, described_class.new(game_plane).recommended_orientation
end

def test_correctly_recommends_square_block_any_orientation
game_plane = <<~PLANE


▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
PLANE
instance = described_class.new(game_plane)
assert_equal 'Square', instance.recommended_block
assert_equal :any, instance.recommended_orientation
end

def test_correctly_recommends_square_block_deep
game_plane = <<~PLANE


▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
PLANE
instance = described_class.new(game_plane)
assert_equal 'Square', instance.recommended_block
assert_equal :any, instance.recommended_orientation
end

def test_correctly_recommends_line_block_orientation_1
game_plane = <<~PLANE


▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
PLANE
instance = described_class.new(game_plane)
assert_equal 'Line', instance.recommended_block
assert_equal :orientation_1, instance.recommended_orientation
end

def test_correctly_recommends_line_block_orientation_2
game_plane = <<~PLANE


▓▓▓▓▓▓▓▓▓▓ ▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
PLANE
instance = described_class.new(game_plane)
assert_equal 'Line', instance.recommended_block
assert_equal :orientation_2, instance.recommended_orientation
end

def test_correctly_recommends_z_block_orientation_1
game_plane = <<~PLANE


▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
PLANE
instance = described_class.new(game_plane)
assert_equal 'Z', instance.recommended_block
assert_equal :orientation_1, instance.recommended_orientation
end

def test_correctly_recommends_z_block_orientation_2
game_plane = <<~PLANE


▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
PLANE
instance = described_class.new(game_plane)
assert_equal 'Z', instance.recommended_block
assert_equal :orientation_2, instance.recommended_orientation
end

def test_correctly_recommends_l_orientation_2
game_plane = <<~PLANE


▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓
▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
PLANE
instance = described_class.new(game_plane)
assert_equal 'L', instance.recommended_block
assert_equal :orientation_2, instance.recommended_orientation
end

def test_correctly_recommends_l_orientation_3
game_plane = <<~PLANE


▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
PLANE
instance = described_class.new(game_plane)
assert_equal 'L', instance.recommended_block
assert_equal :orientation_3, instance.recommended_orientation
end

def test_correctly_recommends_l_orientation_4
game_plane = <<~PLANE


▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
PLANE
instance = described_class.new(game_plane)
assert_equal 'L', instance.recommended_block
assert_equal :orientation_4, instance.recommended_orientation
end

def test_correctly_recommends_n_block_orientation_1
game_plane = <<~PLANE


▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓
▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
PLANE
instance = described_class.new(game_plane)
assert_equal 'N', instance.recommended_block
assert_equal :orientation_1, instance.recommended_orientation
end

def test_correctly_recommends_n_block_orientation_2
game_plane = <<~PLANE


▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
PLANE
instance = described_class.new(game_plane)
assert_equal 'N', instance.recommended_block
assert_equal :orientation_2, instance.recommended_orientation
end


private

def described_class
Solver
end
end
Loading