TNTsweeper_app.lua 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. local level_config = {
  2. ['Small'] = { w = 9, h = 9, bomb = 10, icon_size = 0.9 },
  3. ['Small hard'] = { w = 9, h = 9, bomb = 35, icon_size = 0.9},
  4. ['Midsize'] = { w = 16, h = 16, bomb = 40, icon_size = 0.7 },
  5. ['Midsize hard'] = { w = 16, h = 16, bomb = 99, icon_size = 0.7},
  6. ['Big'] = { w = 24, h = 20, bomb = 99, icon_size = 0.58 },
  7. ['Big hard'] = { w = 24, h = 20, bomb = 170, icon_size = 0.58},
  8. }
  9. local sweeper_class = {}
  10. sweeper_class.__index = sweeper_class
  11. function sweeper_class:init(level)
  12. self.data.level = level
  13. if not level_config[level] then print('ERROR: WRONG LEVEL', level) end
  14. local config = level_config[level]
  15. -- build board
  16. self.data.board = {}
  17. self.data.open_all = config.w * config.h - config.bomb
  18. self.data.open_count = 0
  19. self.data.bomb_all = config.bomb
  20. self.data.bomb_count = 0
  21. local board = self.data.board
  22. for w = 1, config.w do
  23. board[w] = {}
  24. for h = 1, config.h do
  25. board[w][h] = { count = 0, is_bomb = false, is_revealed = false }
  26. end
  27. end
  28. -- fill board with bombs
  29. local placed = 0
  30. while placed < config.bomb do
  31. local rnd_w = math.random(config.w)
  32. local rnd_h = math.random(config.h)
  33. local rndfield = board[rnd_w][rnd_h]
  34. if not rndfield.is_bomb then
  35. rndfield.is_bomb = true
  36. placed = placed + 1
  37. for w = rnd_w - 1, rnd_w + 1 do
  38. for h = rnd_h - 1, rnd_h + 1 do
  39. if board[w] and board[w][h] then
  40. board[w][h].count = board[w][h].count + 1
  41. end
  42. end
  43. end
  44. end
  45. end
  46. end
  47. function sweeper_class:get(sel_w, sel_h)
  48. local board = self.data.board
  49. if board[sel_w] then
  50. return board[sel_w][sel_h]
  51. end
  52. end
  53. function sweeper_class:reveal(sel_w, sel_h)
  54. local board = self.data.board
  55. local sel = self:get(sel_w, sel_h)
  56. -- unmark bomb
  57. if sel.bomb_marked then
  58. self:toggle_bomb_mark(sel_w, sel_h)
  59. end
  60. sel.is_revealed = true
  61. if sel.is_bomb then
  62. -- Bomb found
  63. self.data.bomb_count = self.data.bomb_count + 1
  64. else
  65. -- No bomb, count empty fields
  66. self.data.open_count = self.data.open_count + 1
  67. if sel.count == 0 then
  68. for w = sel_w - 1, sel_w + 1 do
  69. for h = sel_h - 1, sel_h + 1 do
  70. local chk_sel = self:get(w,h)
  71. if chk_sel and
  72. not chk_sel.is_revealed and
  73. not chk_sel.bomb_marked then
  74. self:reveal(w,h)
  75. end
  76. end
  77. end
  78. end
  79. end
  80. end
  81. function sweeper_class:toggle_bomb_mark(sel_w, sel_h)
  82. local sel = self:get(sel_w, sel_h)
  83. if sel.bomb_marked then
  84. self.data.bomb_count = self.data.bomb_count - 1
  85. sel.bomb_marked = nil
  86. else
  87. self.data.bomb_count = self.data.bomb_count + 1
  88. sel.bomb_marked = true
  89. end
  90. end
  91. local function get_sweeper(data)
  92. local self = setmetatable({}, sweeper_class)
  93. self.data = data
  94. return self
  95. end
  96. laptop.register_app("tntsweeper", {
  97. app_name = "TNT Sweeper",
  98. app_icon = "laptop_tnt.png",
  99. app_info = "Avoid hitting TNT",
  100. formspec_func = function(app, mtos)
  101. local data = mtos.bdev:get_app_storage('ram', 'tntsweeper')
  102. local sweeper = get_sweeper(data)
  103. if not sweeper.data.level then
  104. sweeper:init('Midsize')
  105. end
  106. local config = level_config[sweeper.data.level]
  107. local formspec = ""
  108. for w = 1, config.w do
  109. for h = 1, config.h do
  110. local pos = (w*config.icon_size*0.8)..','..(h*config.icon_size*0.85)
  111. local field = sweeper.data.board[w][h]
  112. if not field.is_revealed then
  113. if field.bomb_marked then
  114. formspec = formspec .. "image_button["..pos..";"..config.icon_size..","..config.icon_size..";"..mtos.theme.minor_button.."^"..mtos.theme:get_texture("laptop_tnt.png")..";field:"..w..":"..h..";]"
  115. else
  116. formspec = formspec .. "image_button["..pos..";"..config.icon_size..","..config.icon_size..";"..mtos.theme.minor_button..";field:"..w..":"..h..";]"
  117. end
  118. elseif field.is_bomb then
  119. formspec = formspec .. "image["..pos..";"..config.icon_size..","..config.icon_size..";"..mtos.theme:get_texture("laptop_boom.png").."]"
  120. elseif field.count > 0 then
  121. local lbpos = ((w+0.4)*config.icon_size*0.8)..','..((h+0.1)*config.icon_size*0.85)
  122. formspec = formspec .. mtos.theme:get_label(lbpos, field.count)
  123. end
  124. end
  125. end
  126. formspec = formspec .. "background[12,0.5;3,1;"..mtos.theme.contrast_background..']'..
  127. mtos.theme:get_label("12,0.5", "Open fields: "..sweeper.data.open_count.."/"..sweeper.data.open_all, "contrast")..
  128. mtos.theme:get_label("12,1", "Bomb: "..sweeper.data.bomb_count.."/"..sweeper.data.bomb_all, "contrast")..
  129. mtos.theme:get_button('12.5,2;1.5,0.8', 'major', 'reset', 'Small')..
  130. mtos.theme:get_button('12.5,3;1.5,0.8', 'major', 'reset', 'Small hard')..
  131. mtos.theme:get_button('12.5,4;1.5,0.8', 'major', 'reset', 'Midsize')..
  132. mtos.theme:get_button('12.5,5;1.5,0.8', 'major', 'reset', 'Midsize hard')..
  133. mtos.theme:get_button('12.5,6;1.5,0.8', 'major', 'reset', 'Big')..
  134. mtos.theme:get_button('12.5,7;1.5,0.8', 'major', 'reset', 'Big hard')
  135. if data.mark_mode then
  136. formspec = formspec .. mtos.theme:get_button('12.5,9;1.5,0.8', 'minor', 'mark_mode', 'mark', 'change to reveal mode')
  137. else
  138. formspec = formspec .. mtos.theme:get_button('12.5,9;1.5,0.8', 'minor', 'mark_mode', 'reveal', 'change to mark mode')
  139. end
  140. return formspec
  141. end,
  142. receive_fields_func = function(app, mtos, sender, fields)
  143. local data = mtos.bdev:get_app_storage('ram', 'tntsweeper')
  144. local sweeper = get_sweeper(data)
  145. local config = level_config[sweeper.data.level]
  146. for field, _ in pairs(fields) do
  147. if field:sub(1,6) == 'field:' then
  148. local sel_w, sel_h
  149. for str in field:gmatch("([^:]+)") do
  150. if str ~= 'field' then
  151. if not sel_w then
  152. sel_w = tonumber(str)
  153. else
  154. sel_h = tonumber(str)
  155. end
  156. end
  157. end
  158. --if sender:get_player_control().sneak then -- TODO: test if https://github.com/minetest/minetest/issues/6353
  159. if data.mark_mode then
  160. sweeper:toggle_bomb_mark(sel_w, sel_h)
  161. else
  162. sweeper:reveal(sel_w, sel_h)
  163. end
  164. end
  165. end
  166. if fields.reset then
  167. sweeper:init(minetest.strip_colors(fields.reset))
  168. elseif fields.mark_mode then
  169. if data.mark_mode then
  170. data.mark_mode = nil
  171. else
  172. data.mark_mode = true
  173. end
  174. end
  175. end
  176. })