Issue with minesweeper like game

I am working on a minesweeper game, problem is well, just look at the image:

The tiles you see are physical parts with decals on them they are named 1-81 from the top right down. so the very top right on is 1 and the very bottom left one is 81.

The tiles have numbers displayed even if a bomb isnt next to them as it will look to the next tile name, and if thats in the top of bottom of another row it will be wrong.

I know the code has done nothing to account for that, that’s because I dont even know where to begin doing something like that.

here is the code:

local player = game.Players.LocalPlayer
local char = player.Character or player.CharacterAdded:Wait()

local Tiles = game.Workspace:WaitForChild('Tiles')

local MineAmmount = 5

local FirstClick = true
local mineExcept

local images = {
	["0"] = "rbxassetid://116277024059622",
	["1"] = "rbxassetid://86859798607558",
	["2"] = "rbxassetid://112380134886854",
	["3"] = "rbxassetid://91719019875877",
	["4"] = "rbxassetid://137217826661977",
	["5"] = "rbxassetid://121337137503859",
	["6"] = "rbxassetid://123633267039417",
	["7"] = "rbxassetid://140313058714412",
	["8"] = "rbxassetid://88648128062491",
	["Mine"] = "rbxassetid://6737405081",
	["Flag"] = "rbxassetid://10720664742"
}


function assignMine()
	local chosenTile = math.random(1,81)
	if Tiles[chosenTile].Mined.Value ~= true and Tiles[chosenTile].Name ~= mineExcept then
		Tiles[chosenTile].Mined.Value = true
		--Tiles[chosenTile].Decal.Texture = images["0"]
	else
		assignMine()
	end
end

function spawnMines()
	---for i, tile in pairs(Tiles:GetChildren()) do
	--	tile.Decal.Texture = images["0"]
	--end
	for i = 1, MineAmmount do
		assignMine()
	end
end

function getMinesNearbyAll()
	for i, tile in pairs(Tiles:GetChildren()) do
		if tile.Mined.Value == true then
			
		else
			local tileName = tonumber(tile.Name) 
			local minesNearby = 0

			local positionsToCheck = {-1, 1, -9, 9, -10, 10, -8, 8}


			for _, offset in ipairs(positionsToCheck) do
				local neighborTileName = tileName + offset
				local neighborTile = Tiles:FindFirstChild(tostring(neighborTileName))  

				if neighborTile and neighborTile.Mined.Value == true then
					minesNearby = minesNearby + 1
				end
			end


			tile.MinesNearby.Value = minesNearby
		end	 
	end
end

function revealAll()
	for i, tile in pairs(Tiles:GetChildren()) do
		if tile.Mined.Value == true then
			tile.Decal.Texture = images["Mine"]
		else
			local tileName = tonumber(tile.Name) 
			local minesNearby = 0

			local positionsToCheck = {-1, 1, -9, 9, -10, 10, -8, 8}


			for _, offset in ipairs(positionsToCheck) do
				local neighborTileName = tileName + offset
				local neighborTile = Tiles:FindFirstChild(tostring(neighborTileName))  

				if neighborTile and neighborTile.Mined.Value == true then
					minesNearby = minesNearby + 1
				end
			end
			

			tile.Decal.Texture = images[tostring(minesNearby)] or images["0"]
		end	 
	end

end


function floodReveal(tile)
	local alreadyChecked = {}

	local positionsToCheck = {-1, 1, -9, 9, -10, 10, -8, 8}

	local minesNearby = 0

	local function get(localTile)
		print("get function fired")
		local tileName = tonumber(localTile.Name)

		for _, offset in ipairs(positionsToCheck) do
			local neighborTileName = tileName + offset
			local neighborTile = Tiles:FindFirstChild(tostring(neighborTileName))  

			if neighborTile and alreadyChecked[neighborTileName] == nil then
				--if rayCast(localTile, neighborTile) then
					if neighborTile.Mined.Value == false then
						local nearby = getMineNearby(neighborTile, "true")
						print(nearby)
						neighborTile.Decal.Texture = images[tostring(nearby)]
						alreadyChecked[neighborTileName] = true
						task.spawn(get, neighborTile)
				end
			--end
		end
	end
end

get(tile)

end

function rayCast(start, target)
	local rayDirection = target.Position - start.Position
	local rayParams = RaycastParams.new()
	rayParams.FilterDescendantsInstances = {start}  
	
	local rayResult = workspace:Raycast(start.Position, rayDirection, rayParams)
	
	if rayResult.Instance then
		return true
	else
		return false
	end
end


function getMineNearby(tile, instruction)
	
	if tile.Mined.Value == true then
		tile.Decal.Texture = images["Mine"]
		revealAll()
	else
		local tileName = tonumber(tile.Name) 
		local minesNearby = 0
		local tileRow = tile.Row.Value
	
		local positionsToCheck = {-1, 1, -9, 9, -10, 10, -8, 8}


		for _, offset in ipairs(positionsToCheck) do
			local neighborTileName = tileName + offset
			local neighborTile = Tiles:FindFirstChild(tostring(neighborTileName))  

			if neighborTile and neighborTile.Mined.Value == true then
				minesNearby = minesNearby + 1
			end
		end

		if minesNearby == 0 and instruction == "false" then
			tile.Decal.Texture = images["0"]
			floodReveal(tile)
		else
			tile.Decal.Texture = images[tostring(minesNearby)]
			return minesNearby
		end
	end
end

for i, CD in pairs(Tiles:GetDescendants()) do
	if CD:IsA("ClickDetector") then
		CD.MouseClick:Connect(function()
			if FirstClick == true then
				FirstClick = false
				mineExcept = CD.Parent.Name
				spawnMines()
				getMinesNearbyAll()
				task.wait(1)
				getMineNearby(CD.Parent, "false")
			else
				print('left click')
				getMineNearby(CD.Parent, "false")
			end
		end)
		CD.RightMouseClick:Connect(function()
			print("right click")
			if CD.Parent.Decal.Texture == "" then
				CD.Parent.Decal.Texture = images["Flag"]
			elseif CD.Parent.Decal.Texture == images["Flag"] then
				CD.Parent.Decal.Texture = ""
			end
		end)
	end
end

another example:

It seems that the algorithm for finding mines seems to loop around to the opposite side when near the border? Is this the issue? You dont want it to loop back?

Yes that is the issue, problem is I dont even know where i will begin to fix something like that

all besides that one 2 at the bottom right is as it should be. All other tiles display correct numbers

My guess for now is that its an error in math when finding surrounding tiles, because for example if a tile is at the right edge and tries to look one to the right it does +1 to itself and jumps down a row and and all the way to the left side, atleast from what i am seeing from the script. i would definitely investigate there.

Yes; all the tiles are named are numbered 1 to 81. What’s it doing isn’t a bug it’s something I knew would happen but don’t know how to avoid

tiles should have been stored in a grid with x and y separately like (5, 5) and (8, 9)

then if you put the tiles into a table its much easier to work with
for example, grid[x-1][y] would be the left tile

1 Like

I have not thought about that, Ill try that, but it sounds promising

I would redesign how the tiles are handled, you can put the parts into folders so as to simulate an array. And locate the tiles using X Y coordinates instead of numbered indexes. Knowing the dimensions of the board you can then ignore any tile checks that are beyond said dimension on any axis or that are less than zero.

Hahah beat me to it… This is the proper way

local player = game.Players.LocalPlayer
local char = player.Character or player.CharacterAdded:Wait()

local Tiles = game.Workspace:WaitForChild('Tiles')

local MineAmmount = 5

local FirstClick = true
local mineExcept

local images = {
	["0"] = "rbxassetid://116277024059622",
	["1"] = "rbxassetid://86859798607558",
	["2"] = "rbxassetid://112380134886854",
	["3"] = "rbxassetid://91719019875877",
	["4"] = "rbxassetid://137217826661977",
	["5"] = "rbxassetid://121337137503859",
	["6"] = "rbxassetid://123633267039417",
	["7"] = "rbxassetid://140313058714412",
	["8"] = "rbxassetid://88648128062491",
	["Mine"] = "rbxassetid://6737405081",
	["Flag"] = "rbxassetid://10720664742"
}

local CoOrdinates = {
	["1"] = {x = 1, y = 1}, ["2"] = {x = 1, y = 2}, ["3"] = {x = 1, y = 3}, ["4"] = {x = 1, y = 4}, ["5"] = {x = 1, y = 5}, ["6"] = {x = 1, y = 6}, ["7"] = {x = 1, y = 7}, ["8"] = {x = 1, y = 8}, ["9"] = {x = 1, y = 9},
	["10"] = {x = 2, y = 1}, ["11"] = {x = 2, y = 2}, ["12"] = {x = 2, y = 3}, ["13"] = {x = 2, y = 4}, ["14"] = {x = 2, y = 5}, ["15"] = {x = 2, y = 6}, ["16"] = {x = 2, y = 7}, ["17"] = {x = 2, y = 8}, ["18"] = {x = 2, y = 9},
	["19"] = {x = 3, y = 1}, ["20"] = {x = 3, y = 2}, ["21"] = {x = 3, y = 3}, ["22"] = {x = 3, y = 4}, ["23"] = {x = 3, y = 5}, ["24"] = {x = 3, y = 6}, ["25"] = {x = 3, y = 7}, ["26"] = {x = 3, y = 8}, ["27"] = {x = 3, y = 9},
	["28"] = {x = 4, y = 1}, ["29"] = {x = 4, y = 2}, ["30"] = {x = 4, y = 3}, ["31"] = {x = 4, y = 4}, ["32"] = {x = 4, y = 5}, ["33"] = {x = 4, y = 6}, ["34"] = {x = 4, y = 7}, ["35"] = {x = 4, y = 8}, ["36"] = {x = 4, y = 9},
	["37"] = {x = 5, y = 1}, ["38"] = {x = 5, y = 2}, ["39"] = {x = 5, y = 3}, ["40"] = {x = 5, y = 4}, ["41"] = {x = 5, y = 5}, ["42"] = {x = 5, y = 6}, ["43"] = {x = 5, y = 7}, ["44"] = {x = 5, y = 8}, ["45"] = {x = 5, y = 9},
	["46"] = {x = 6, y = 1}, ["47"] = {x = 6, y = 2}, ["48"] = {x = 6, y = 3}, ["49"] = {x = 6, y = 4}, ["50"] = {x = 6, y = 5}, ["51"] = {x = 6, y = 6}, ["52"] = {x = 6, y = 7}, ["53"] = {x = 6, y = 8}, ["54"] = {x = 6, y = 9},
	["55"] = {x = 7, y = 1}, ["56"] = {x = 7, y = 2}, ["57"] = {x = 7, y = 3}, ["58"] = {x = 7, y = 4}, ["59"] = {x = 7, y = 5}, ["60"] = {x = 7, y = 6}, ["61"] = {x = 7, y = 7}, ["62"] = {x = 7, y = 8}, ["63"] = {x = 7, y = 9},
	["64"] = {x = 8, y = 1}, ["65"] = {x = 8, y = 2}, ["66"] = {x = 8, y = 3}, ["67"] = {x = 8, y = 4}, ["68"] = {x = 8, y = 5}, ["69"] = {x = 8, y = 6}, ["70"] = {x = 8, y = 7}, ["71"] = {x = 8, y = 8}, ["72"] = {x = 8, y = 9},
	["73"] = {x = 9, y = 1}, ["74"] = {x = 9, y = 2}, ["75"] = {x = 9, y = 3}, ["76"] = {x = 9, y = 4}, ["77"] = {x = 9, y = 5}, ["78"] = {x = 9, y = 6}, ["79"] = {x = 9, y = 7}, ["80"] = {x = 9, y = 8}, ["81"] = {x = 9, y = 9}
}



function assignMine()
	local chosenX = math.random(1,9)
	local chosenY = math.random(1,2)
	if Tiles[chosenY..","..chosenX].Mined.Value ~= true and Tiles[chosenY..","..chosenX].Name ~= mineExcept then
		Tiles[chosenY..","..chosenX].Mined.Value = true
	else
		assignMine()
	end
end

function spawnMines()
	for i = 1, MineAmmount do
		assignMine()
	end
end

function nearbyMinesMaster(mine)
	local nearbyMines = 0

	local split = string.split(mine, ",")
	local XCordinate = tonumber(split[1])
	local YCordinate = tonumber(split[2])
	print(XCordinate .. "," .. YCordinate)

	-- Check each of the 8 neighboring tiles using FindFirstChild
	if Tiles:FindFirstChild((YCordinate-1) .. "," .. XCordinate) then
		if Tiles[(YCordinate-1) .. "," .. XCordinate].Mined.Value == true then
			nearbyMines += 1
			print("here1")
		end
	end
	if Tiles:FindFirstChild((YCordinate+1) .. "," .. XCordinate) then
		if Tiles[(YCordinate+1) .. "," .. XCordinate].Mined.Value == true then
			nearbyMines += 1
			print("here2")
		end
	end
	if Tiles:FindFirstChild(YCordinate .. "," .. (XCordinate+1)) then
		if Tiles[YCordinate .. "," .. (XCordinate+1)].Mined.Value == true then
			nearbyMines += 1
			print("here3")
		end
	end
	if Tiles:FindFirstChild(YCordinate .. "," .. (XCordinate-1)) then
		if Tiles[YCordinate .. "," .. (XCordinate-1)].Mined.Value == true then
			nearbyMines += 1
			print("here4")
		end
	end
	if Tiles:FindFirstChild((YCordinate+1) .. "," .. (XCordinate+1)) then
		if Tiles[(YCordinate+1) .. "," .. (XCordinate+1)].Mined.Value == true then
			nearbyMines += 1
			print("here5")
		end
	end
	if Tiles:FindFirstChild((YCordinate-1) .. "," .. (XCordinate-1)) then
		if Tiles[(YCordinate-1) .. "," .. (XCordinate-1)].Mined.Value == true then
			nearbyMines += 1
			print("here6")
		end
	end
	if Tiles:FindFirstChild((YCordinate-1) .. "," .. (XCordinate+1)) then
		if Tiles[(YCordinate-1) .. "," .. (XCordinate+1)].Mined.Value == true then
			nearbyMines += 1
			print("here7")
		end
	end
	if Tiles:FindFirstChild((YCordinate+1) .. "," .. (XCordinate-1)) then
		if Tiles[(YCordinate+1) .. "," .. (XCordinate-1)].Mined.Value == true then
			nearbyMines += 1
			print("here8")
		end
	end

	print(nearbyMines)
	return nearbyMines
end




function revealAll()
	for i, tile in pairs(Tiles:GetChildren()) do
		if tile.Mined.Value == true then
			tile.Decal.Texture = images["Mine"]
		else
			
			local neabyMines = nearbyMinesMaster(tile.Name)

			tile.Decal.Texture = images[tostring(neabyMines)]
		end
	end
end

function floodRevealOld(tile)
	local alreadyChecked = {}
	local positionsToCheck = {-1, 1, -9, 9, -10, 10, -8, 8}

	local function get(localTile)
		alreadyChecked[localTile.Name] = true
		for _, offset in ipairs(positionsToCheck) do
			local neighborTileName = tonumber(localTile.Name) + offset
			local neighborTile = Tiles:FindFirstChild(tostring(neighborTileName))

			if neighborTile and not alreadyChecked[neighborTile.Name] then
				if neighborTile.Mined.Value == false then
					local nearby = getMineNearby(neighborTile, "true")
					neighborTile.Decal.Texture = images[tostring(nearby)]
					alreadyChecked[neighborTile.Name] = true

					if nearby == 0 then
						task.spawn(get, neighborTile)
					end
				end
			end
		end
	end

	get(tile)
end

function floodReveal(tile)
	local alreadyChecked = {}
	
	local function get(localTile)
		local minesNearby = nearbyMinesMaster(localTile.Name)
	end
	
	get(tile)
end

function getMineNearby(tile)
	if tile.Mined.Value == true then
		tile.Decal.Texture = images["Mine"]
		revealAll()
	else
		local minesNearby = nearbyMinesMaster(tile.Name)
		
		if minesNearby == 0 then
			tile.Decal.Texture = images["0"]
		--	floodReveal(tile)
		else
			tile.Decal.Texture = images[tostring(minesNearby)]
			return minesNearby
		end
	end
end


function checkForWin()

	local correct = 0

	for i, v in pairs(Tiles:GetChildren()) do
		if v.Mined.Value == true and v.Decal.Texture == images["Flag"] then
			correct += 1
		end
	end
	if correct == MineAmmount then
		print("you won")
	end
end

for i, CD in pairs(Tiles:GetDescendants()) do
	if CD:IsA("ClickDetector") then
		CD.MouseClick:Connect(function()
			if FirstClick == true then
				FirstClick = false
				mineExcept = CD.Parent.Name
				spawnMines()
				
				task.wait(4)
				revealAll()
			--	getMineNearby(CD.Parent, "false")
			--	floodReveal()
			else
				checkForWin()
				getMineNearby(CD.Parent, "false")
			end
		end)

		CD.RightMouseClick:Connect(function()
			if CD.Parent.Decal.Texture == "" then
				CD.Parent.Decal.Texture = images["Flag"]
				checkForWin()
			elseif CD.Parent.Decal.Texture == images["Flag"] then
				CD.Parent.Decal.Texture = ""
				checkForWin()
			end
		end)
	end
end

what am i doing wrong?

Can try something like this with the original code.
In a function like your revealAll(), you store the tile number in tileName and create a positionsToCheck list for each tile. You could create that list with a function like the one below.

local positionsToCheck = getPositionsToCheck(tileName) -- returns list to check

local gridSize = 9	-- assumes square grid

--[[
	Assumes square grid with numbering starting at top right going down
	Returns table with positions to check
]]
local function getPositionsToCheck(n)
	local tmp = {}
	
	-- calc row and col from n and gridSize
	-- row 0 at top, col 0 on right
	local row = (n-1) % gridSize
	local col = math.floor((n-1) / gridSize)
	
	-- save bool flag for below
	local rt = col == 0
	local lt = col == gridSize-1
	
	-- load valid values to check into table
	if not lt then table.insert(tmp, gridSize) end	-- 9
	if not rt then table.insert(tmp, -gridSize) end	-- -9
	
	if row ~= 0 then	-- not top row
		table.insert(tmp, -1)						-- -1
		if not lt then table.insert(tmp, gridSize-1) end	-- 8	
		if not rt then table.insert(tmp, -gridSize-1) end	-- -10
	end
	
	if row ~= gridSize-1 then	-- not bottom row
		table.insert(tmp, 1)						-- 1
		if not lt then table.insert(tmp, gridSize+1) end	-- 10
		if not rt then table.insert(tmp, -gridSize+1) end	-- -8
	end
	
	return tmp
end

I already fixed it and finished the whole game but thanks for the reply

1 Like

You reallllyyy need to learn to use for Loops no need to write sooo much code, when it can be done in like few lines. Automation is key to performant code. The first Dictionary can be generated with 2 for loops and the location checks is just a simple for ( i = -1,1,1)

Mark Something as solution then dont leave the thread open