- <% @board.board_state.each do |row| %>
+ <% @board.height.times do |y| %>
- <% row.each do |cell| %>
-
- <%= cell == '*' ? "💣" : "⬜️" %>
-
+ <% @board.width.times do |x| %>
+ <% if cell_contains_mine?(x, y, @mines) %>
+
+ 💣
+
+ <% else %>
+
+ ⬜️
+
+ <% end %>
<% end %>
<% end %>
diff --git a/db/migrate/20230406000648_create_mines.rb b/db/migrate/20230406000648_create_mines.rb
new file mode 100644
index 0000000..ac5396f
--- /dev/null
+++ b/db/migrate/20230406000648_create_mines.rb
@@ -0,0 +1,11 @@
+class CreateMines < ActiveRecord::Migration[7.0]
+ def change
+ create_table :mines do |t|
+ t.references :board, null: false, foreign_key: true
+ t.integer :x
+ t.integer :y
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20230406002200_remove_board_state_column_from_boards.rb b/db/migrate/20230406002200_remove_board_state_column_from_boards.rb
new file mode 100644
index 0000000..82f83fd
--- /dev/null
+++ b/db/migrate/20230406002200_remove_board_state_column_from_boards.rb
@@ -0,0 +1,5 @@
+class RemoveBoardStateColumnFromBoards < ActiveRecord::Migration[7.0]
+ def change
+ remove_column :boards, :board_state
+ end
+end
diff --git a/db/migrate/20230406002317_rename_mines_column_in_boards.rb b/db/migrate/20230406002317_rename_mines_column_in_boards.rb
new file mode 100644
index 0000000..6c8f957
--- /dev/null
+++ b/db/migrate/20230406002317_rename_mines_column_in_boards.rb
@@ -0,0 +1,5 @@
+class RenameMinesColumnInBoards < ActiveRecord::Migration[7.0]
+ def change
+ rename_column :boards, :mines, :mine_count
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 553a4e6..19d1c76 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.0].define(version: 2023_04_03_121704) do
+ActiveRecord::Schema[7.0].define(version: 2023_04_06_002317) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -18,14 +18,22 @@
t.string "name"
t.integer "width"
t.integer "height"
- t.integer "mines"
+ t.integer "mine_count"
t.bigint "user_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
- t.text "board_state"
t.index ["user_id"], name: "index_boards_on_user_id"
end
+ create_table "mines", force: :cascade do |t|
+ t.bigint "board_id", null: false
+ t.integer "x"
+ t.integer "y"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["board_id"], name: "index_mines_on_board_id"
+ end
+
create_table "users", force: :cascade do |t|
t.string "email"
t.datetime "created_at", null: false
@@ -33,4 +41,5 @@
end
add_foreign_key "boards", "users"
+ add_foreign_key "mines", "boards"
end
diff --git a/lib/tasks/update_boards.rake b/lib/tasks/update_boards.rake
new file mode 100644
index 0000000..2608313
--- /dev/null
+++ b/lib/tasks/update_boards.rake
@@ -0,0 +1,21 @@
+namespace :boards do
+ desc 'Update previously generated boards to work with the new approach'
+ task update_boards: :environment do
+ boards = Board.includes(:mines).all
+
+ boards.each do |board|
+ if board.mines.size.zero? && board.mine_count > 0
+ generator = MineGeneratorService.new(board.width, board.height, board.mine_count)
+ mine_positions = generator.generate_mines
+
+ mine_positions.each do |x, y|
+ Mine.create(board: board, x: x, y: y)
+ end
+
+ puts "Updated board #{board.id}"
+ end
+ end
+
+ puts 'All boards updated'
+ end
+end
diff --git a/test/controllers/boards_controller_test.rb b/test/controllers/boards_controller_test.rb
index fdb0ddc..c5082ad 100644
--- a/test/controllers/boards_controller_test.rb
+++ b/test/controllers/boards_controller_test.rb
@@ -8,14 +8,14 @@ class BoardsControllerTest < ActionDispatch::IntegrationTest
test "should create board" do
assert_difference('Board.count', 1) do
- post boards_url, params: { board: { email: 'test@example.com', name: 'Test Board', width: 10, height: 10, mines: 10 } }
+ post boards_url, params: { board: { email: 'test@example.com', name: 'Test Board', width: 10, height: 10, mine_count: 10 } }
end
assert_redirected_to board_path(Board.last)
end
test "should not create board with invalid data" do
assert_no_difference('Board.count') do
- post boards_url, params: { board: { email: 'test@example.com', name: '', width: 10, height: 10, mines: 10 } }
+ post boards_url, params: { board: { email: 'test@example.com', name: '', width: 10, height: 10, mine_count: 10 } }
end
assert_response :unprocessable_entity
end
diff --git a/test/fixtures/boards.yml b/test/fixtures/boards.yml
index e66d7cb..e5c796a 100644
--- a/test/fixtures/boards.yml
+++ b/test/fixtures/boards.yml
@@ -3,13 +3,11 @@ one:
name: MyString
width: 5
height: 5
- mines: 5
- board_state: [["", "", "", "*", ""], ["", "", "*", "", ""], ["", "", "", "", ""], ["", "*", "", "*", ""], ["", "", "", "*", ""]]
+ mine_count: 5
two:
user: one
name: AnotherBoard
width: 5
height: 5
- mines: 5
- board_state: [["", "", "", "*", ""], ["", "", "*", "", ""], ["", "", "", "", ""], ["", "*", "", "*", ""], ["", "", "", "*", ""]]
+ mine_count: 5
diff --git a/test/fixtures/mines.yml b/test/fixtures/mines.yml
new file mode 100644
index 0000000..bb36865
--- /dev/null
+++ b/test/fixtures/mines.yml
@@ -0,0 +1,11 @@
+# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+one:
+ board: one
+ x: 1
+ 'y': 1
+
+two:
+ board: two
+ x: 1
+ 'y': 1
diff --git a/test/models/board_test.rb b/test/models/board_test.rb
index 6b32c86..94d5f07 100644
--- a/test/models/board_test.rb
+++ b/test/models/board_test.rb
@@ -3,87 +3,126 @@
class BoardTest < ActiveSupport::TestCase
setup do
@user = users(:one)
+ @board = boards(:one)
+ @board.user = @user
end
- test "should be valid" do
- board = Board.new(user: @user, name: 'Test Board', width: 10, height: 10, mines: 10)
- assert board.valid?
+ test 'valid board' do
+ assert @board.valid?
end
- test "should generate board with correct dimensions" do
- board = Board.new(user: @user, name: 'Test Board', width: 10, height: 10, mines: 10)
-
- board.generate_board
- assert_equal board.height, board.board_state.length, "Board has incorrect height"
- assert_equal board.width, board.board_state.first.length, "Board has incorrect width"
+ test 'invalid without width' do
+ @board.width = nil
+ refute @board.valid?, 'board is valid without a width'
+ assert_not_nil @board.errors[:width], 'no validation error for width'
end
- test "should generate board with correct number of mines" do
- board = Board.new(user: @user, name: 'Test Board', width: 10, height: 10, mines: 10)
-
- board.generate_board
- mine_count = board.board_state.flatten.count('*')
- assert_equal board.mines, mine_count, "Board has incorrect number of mines"
+ test 'invalid without height' do
+ @board.height = nil
+ refute @board.valid?, 'board is valid without a height'
+ assert_not_nil @board.errors[:height], 'no validation error for height'
end
- test "should generate empty board with zero mines" do
- board = Board.new(user: @user, name: 'Test Board', width: 10, height: 10, mines: 0)
+ test 'invalid without name' do
+ @board.name = nil
+ refute @board.valid?, 'board is valid without a name'
+ assert_not_nil @board.errors[:name], 'no validation error for name'
+ end
- board.generate_board
- mine_count = board.board_state.flatten.count('*')
- assert_equal 0, mine_count, "Board should have no mines"
+ test 'invalid without user' do
+ @board.user = nil
+ refute @board.valid?, 'board is valid without a user'
+ assert_not_nil @board.errors[:user], 'no validation error for user'
end
- test "should generate full board with all mines" do
- board = Board.new(user: @user, name: 'Test Board', width: 10, height: 10, mines: 100)
+ test 'generate_mines' do
+ @board.mines.destroy_all
+ @board.save
+ @board.generate_board
- board.generate_board
- mine_count = board.board_state.flatten.count('*')
- assert_equal board.mines, mine_count, "Board should have all mines"
- end
+ assert_equal @board.mine_count, @board.mines.count, 'Incorrect number of mines generated'
- test "should be invalid without user" do
- board = Board.new(user: nil, name: 'Test Board', width: 10, height: 10, mines: 10)
- assert_not board.valid?
+ @board.mines.each do |mine|
+ assert mine.x.between?(0, @board.width - 1), 'Mine x position is outside the board'
+ assert mine.y.between?(0, @board.height - 1), 'Mine y position is outside the board'
+ end
end
- test "should be invalid without name" do
- board = Board.new(user: @user, name: nil, width: 10, height: 10, mines: 10)
- assert_not board.valid?
+ test "should be valid" do
+ board = Board.new(user: @user, name: 'Test Board', width: 10, height: 10, mine_count: 10)
+ assert board.valid?
end
- test "should be invalid without width" do
- board = Board.new(user: @user, name: 'Test Board', width: nil, height: 10, mines: 10)
- assert_not board.valid?
+ test "should generate board with correct number of mines" do
+ @board.width = 10
+ @board.height = 10
+ @board.mine_count = 10
+ @board.mines.destroy_all
+
+ @board.generate_board
+ mine_count = @board.mines.count
+ assert_equal @board.mine_count, mine_count, "Board has incorrect number of mines"
end
- test "should be invalid without height" do
- board = Board.new(user: @user, name: 'Test Board', width: 10, height: nil, mines: 10)
- assert_not board.valid?
+ test "should generate empty board with zero mines" do
+ @board.width = 10
+ @board.height = 10
+ @board.mine_count = 0
+ @board.mines.destroy_all
+
+ @board.generate_board
+ mine_count = @board.mines.count
+ assert_equal 0, mine_count, "Board should have no mines"
end
- test "should be invalid without mines" do
- board = Board.new(user: @user, name: 'Test Board', width: 10, height: 10, mines: nil)
- assert_not board.valid?
+ test "should generate full board with all mines" do
+ @board.width = 10
+ @board.height = 10
+ @board.mine_count = 10
+ @board.mines.destroy_all
+
+ @board.generate_board
+ mine_count = @board.mines.count
+ assert_equal @board.mine_count, mine_count, "Board should have all mines"
end
- test "should not allow negative width" do
- board = Board.new(user: @user, name: 'Test Board', width: -10, height: 10, mines: 10)
- assert_not board.valid?
+ test "should generate board efficiently with high mine-to-cell ratio" do
+ @board.width = 100
+ @board.height = 100
+ @board.mine_count = 10000
+ @board.mines.destroy_all
+ start_time = Time.now
+ @board.generate_board
+ end_time = Time.now
+ mine_count = @board.mines.count
+
+ assert_equal @board.mine_count, mine_count, "Board has incorrect number of mines"
+ assert (end_time - start_time) < 2, "Board generation took too long"
end
- test "should not allow negative height" do
- board = Board.new(user: @user, name: 'Test Board', width: 10, height: -10, mines: 10)
- assert_not board.valid?
+ test "should generate board efficiently with low mine-to-cell ratio" do
+ @board.mines.destroy_all
+ @board.width = 100
+ @board.height = 100
+ @board.mine_count = 1
+ start_time = Time.now
+ @board.generate_board
+ end_time = Time.now
+ mine_count = @board.mines.count
+
+ assert_equal @board.mine_count, mine_count, "Board has incorrect number of mines"
+ assert (end_time - start_time) < 1, "Board generation took too long"
end
test "should not allow negative mines" do
- board = Board.new(user: @user, name: 'Test Board', width: 10, height: 10, mines: -10)
- assert_not board.valid?
+ @board.mine_count = -10
+ assert_not @board.valid?
end
test "should not allow more mines than cells" do
- board = Board.new(user: @user, name: 'Test Board', width: 10, height: 10, mines: 101)
- assert_not board.valid?
+ @board.width = 10
+ @board.height = 10
+ @board.mine_count = 101
+ assert_not @board.valid?
end
end
diff --git a/test/models/mine_test.rb b/test/models/mine_test.rb
new file mode 100644
index 0000000..ff95683
--- /dev/null
+++ b/test/models/mine_test.rb
@@ -0,0 +1,7 @@
+require "test_helper"
+
+class MineTest < ActiveSupport::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end