175 lines
4.3 KiB
Ruby
175 lines
4.3 KiB
Ruby
require "xxhash"
|
|
|
|
# Game logic main class
|
|
class GameLogic
|
|
attr_reader :pos
|
|
|
|
def initialize
|
|
@seed = rand(111_111..999_999)
|
|
@pos = {
|
|
board: {},
|
|
lost: {},
|
|
cache: {},
|
|
}
|
|
end
|
|
|
|
# Returns a pseudorandom number between 0 and 100
|
|
def hash(data)
|
|
XXhash.xxh32(data.to_s, @seed) % 100
|
|
end
|
|
|
|
def reset_pos
|
|
@pos.each_value(&:clear)
|
|
end
|
|
|
|
def click(button, g_x, g_y)
|
|
return if clickable?(g_x, g_y)
|
|
case button
|
|
when "left"
|
|
if revealed?(g_x, g_y)
|
|
if count(:mines, g_x, g_y) == count(:flags, g_x, g_y)
|
|
(-1..1).each do |x|
|
|
(-1..1).each do |y|
|
|
next if x.zero? && y.zero?
|
|
reveal(g_x + x, g_y + y)
|
|
end
|
|
end
|
|
end
|
|
else
|
|
reveal(g_x, g_y)
|
|
end
|
|
when "right"
|
|
flag(g_x, g_y)
|
|
end
|
|
end
|
|
|
|
def clickable?(g_x, g_y)
|
|
s_x = (g_x / 9).floor
|
|
s_y = (g_y / 9).floor
|
|
if @pos.lost["#{s_x}:#{s_y}"] ||
|
|
@pos.board.key?("#{s_x}:#{s_y}")
|
|
return false
|
|
end
|
|
(-1..1).each do |x|
|
|
(-1..1).each do |y|
|
|
next if x.zero? && y.zero?
|
|
return true if revealed?(g_x + x, g_y + y) || flagged?(g_x + x, g_y + y)
|
|
end
|
|
end
|
|
false
|
|
end
|
|
|
|
def revealed?(g_x, g_y)
|
|
l_x = ((g_x % 9) + 9) % 9
|
|
l_y = ((g_y % 9) + 9) % 9
|
|
s_x = (g_x / 9).floor
|
|
s_y = (g_y / 9).floor
|
|
return false if @pos.board.key?("#{s_x}:#{s_y}") || @pos.board["#{s_x}:#{s_y}"] == true
|
|
@pos.board["#{s_x}:#{s_y}"][l_x][l_y][1]
|
|
end
|
|
|
|
def flagged?(g_x, g_y)
|
|
l_x = ((g_x % 9) + 9) % 9
|
|
l_y = ((g_y % 9) + 9) % 9
|
|
s_x = (g_x / 9).floor
|
|
s_y = (g_y / 9).floor
|
|
return false if @pos.board.key?("#{s_x}:#{s_y}")
|
|
return true if @pos.board["#{s_x}:#{s_y}"] == true && mine?(g_x, g_y)
|
|
@pos.board["#{s_x}:#{s_y}"][l_x][l_y][2]
|
|
end
|
|
|
|
def reveal(g_x, g_y)
|
|
l_x = ((g_x % 9) + 9) % 9
|
|
l_y = ((g_y % 9) + 9) % 9
|
|
s_x = (g_x / 9).floor
|
|
s_y = (g_y / 9).floor
|
|
|
|
return if @pos.board["#{s_x}:#{s_y}"] == true
|
|
build_sector(s_x, s_y) if @pos.board["#{s_x}:#{s_y}"].nil?
|
|
return if @pos.board["#{s_x}:#{s_y}"][l_x][l_y][1] || @pos.board["#{s_x}:#{s_y}"][l_x][l_y][2]
|
|
|
|
if @pos.board["#{s_x}:#{s_y}"][l_x][l_y][0] == -1
|
|
@pos.lost["#{s_x}:#{s_y}"] = 1
|
|
elsif @pos.board["#{s_x}:#{s_y}"][l_x][l_y][0].zero?
|
|
(-1..1).each do |x|
|
|
(-1..1).each do |y|
|
|
next if x.zero? && y.zero?
|
|
reveal(g_x + x, g_y + y)
|
|
end
|
|
end
|
|
else
|
|
@pos.board["#{s_x}:#{s_y}"][l_x][l_y][1] = true
|
|
end
|
|
|
|
@pos.board["#{s_x}:#{s_y}"] = true if solved?(s_x, s_y)
|
|
end
|
|
|
|
def flag(g_x, g_y)
|
|
l_x = ((g_x % 9) + 9) % 9
|
|
l_y = ((g_y % 9) + 9) % 9
|
|
s_x = (g_x / 9).floor
|
|
s_y = (g_y / 9).floor
|
|
return if @pos.board["#{s_x}:#{s_y}"] == true || revealed?(g_x, g_y)
|
|
build_sector(s_x, s_y) if @pos.board["#{s_x}:#{s_y}"].nil?
|
|
@pos.board["#{s_x}:#{s_y}"][l_x][l_y][2] ^= true
|
|
end
|
|
|
|
def solved?(s_x, s_y)
|
|
(0..8).each do |l_x|
|
|
(0..8).each do |l_y|
|
|
return false if !@pos.board["#{s_x}:#{s_y}"][l_x][l_y][1] && @pos.board["#{s_x}:#{s_y}"][l_x][l_y][0] != -1
|
|
end
|
|
end
|
|
true
|
|
end
|
|
|
|
def count(type, g_x, g_y)
|
|
sum = 0
|
|
(-1..1).each do |x|
|
|
(-1..1).each do |y|
|
|
next if x.zero? && y.zero?
|
|
sum += 1 if case type
|
|
when :mines then mine?(g_x + x, g_y + y)
|
|
when :flags then flagged?(g_x + x, g_y + y)
|
|
else false
|
|
end
|
|
end
|
|
end
|
|
sum
|
|
end
|
|
|
|
def build_sector(s_x, s_y)
|
|
@pos.board["#{s_x}:#{s_y}"] = []
|
|
(0..8).each do |l_x|
|
|
@pos.board["#{s_x}:#{s_y}"][l_x] = []
|
|
(0..8).each do |l_y|
|
|
g_x = l_x + s_x * 9
|
|
g_y = l_y + s_y * 9
|
|
@pos.board["#{s_x}:#{s_y}"][l_x][l_y] = [
|
|
mine?(g_x, g_y) ? -1 : count(:mines, g_x, g_y),
|
|
false, false,
|
|
]
|
|
end
|
|
end
|
|
end
|
|
|
|
def build_cache(s_x, s_y)
|
|
return if @pos.cache["#{s_x}:#{s_y}"]
|
|
return unless @pos.board["#{s_x}:#{s_y}"] == 1
|
|
@pos.cache["#{s_x}:#{s_y}"] = []
|
|
(0..8).each do |l_x|
|
|
@pos.cache["#{s_x}:#{s_y}"][l_x] = []
|
|
(0..8).each do |l_y|
|
|
g_x = l_x + s_x * 9
|
|
g_y = l_y + s_y * 9
|
|
@pos.cache["#{s_x}:#{s_y}"][l_x][l_y] =
|
|
mine?(g_x, g_y) ? [-1, false, true] : [count(:mines, g_x, g_y), true, false]
|
|
end
|
|
end
|
|
end
|
|
|
|
def mine?(g_x, g_y)
|
|
g_x == 4 || g_y == 4 ? false : hash("#{g_x}:#{g_y}") < 17
|
|
end
|
|
end
|