261 lines
10 KiB
Python
Executable File
261 lines
10 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
# mcpipy.com retrieved from URL below, written by Davie Wales
|
|
# https://bitbucket.org/dwales/minesweeper-for-minecraft-pi-edition/src
|
|
|
|
|
|
import sys
|
|
import random
|
|
import threading
|
|
import mcpi.minecraft as minecraft
|
|
import server
|
|
|
|
|
|
defaultDifficulty = 0.1
|
|
setDifficulty = defaultDifficulty
|
|
|
|
class board:
|
|
""" The cartesian grid can be done as follows:
|
|
board = [["?", "*", "?", "?", "*", "?", "?", "?", "?", "*",],
|
|
["?", "?", "*", "?", "?", "?", "?", "?", "?", "?",],
|
|
["?", "*", "*", "?", "*", "?", "?", "*", "*", "?",],
|
|
["?", "?", "?", "?", "?", "?", "*", "?", "*", "?",],
|
|
["*", "?", "?", "?", "?", "*", "?", "?", "?", "?",],
|
|
["?", "?", "?", "?", "?", "?", "?", "?", "?", "?",],
|
|
["?", "*", "?", "*", "?", "?", "?", "*", "?", "*",],
|
|
["?", "?", "?", "?", "?", "?", "?", "?", "?", "?",],
|
|
["?", "*", "?", "?", "*", "*", "?", "?", "*", "?",],
|
|
["?", "?", "*", "?", "?", "?", "?", "*", "?", "?",]]
|
|
|
|
Obviously the grid will be randomly generated on run to fit
|
|
within the bounds of the terminal window.
|
|
Notice that you can access the state of any tile on the grid with
|
|
the simple command "board[x][y]"
|
|
i.e. a = board[0][0] makes a == "?", a = board[0][9] makes a == "*"
|
|
NOTE: I subsequently changed the code to use nested dictionaries,
|
|
rather than lists to represent the board. The general idea is
|
|
still the same...
|
|
If we use a dictionary rather than a list in the following
|
|
function, we will get a KeyError if we try to access a
|
|
negative index, assuming we construct the dictionary such
|
|
that it has identical indexes to the list equivalent.
|
|
i.e. dictionary = {0:{0:" ", 1:" ", 2"*"}, 1:{0:"*", 1:" ", 2:" "}}
|
|
This will be helpful, as it will negate the need to explicitly
|
|
check whether a particular coordinate is legitimate. i.e. a
|
|
dictionary won't match negative values for x, y, but a list will..."""
|
|
|
|
""" At the moment we are getting the window size before curses is
|
|
initialised, which means that we have to use "stty size". If
|
|
we can move this code into the curses section, we can use the
|
|
built in window.getmaxyx(). This will make it easier to use
|
|
windows smaller than the size of the terminal for the game,
|
|
which will in turn allow us to add timers and minecounts."""
|
|
|
|
def __init__(self):
|
|
global width, height
|
|
width = 10
|
|
height = 10
|
|
|
|
def options(self):
|
|
totalTiles = width * height
|
|
|
|
#possible choices of tile: either "*" or " "
|
|
self.mineNumber = int(setDifficulty * totalTiles)
|
|
choices = list(self.mineNumber * "*")
|
|
choices.extend(list((totalTiles-len(choices))*" "))
|
|
return choices
|
|
|
|
# For every x and y, check all the squares around to see if there is a mine,
|
|
# add together the number of mines touching the original square and replace
|
|
# the original square with the final count.
|
|
def numberise(self, board):
|
|
for x in xrange(width):
|
|
for y in xrange(height):
|
|
count = 0
|
|
if board[x][y] != "*":
|
|
for i in xrange(-1, 2):
|
|
for n in xrange(-1, 2):
|
|
try:
|
|
if board[x+i][y+n] == "*":
|
|
count += 1
|
|
except KeyError:
|
|
pass
|
|
if count != 0:
|
|
board[x][y] = str(count)
|
|
|
|
def create(self):
|
|
self.mineCoords = []
|
|
choices = self.options()
|
|
board = {}
|
|
for i in xrange(0, width):
|
|
board[i] = {}
|
|
for n in xrange(0, height):
|
|
board[i][n] = choices.pop(choices.index(random.choice(choices)))
|
|
if board[i][n] == "*":
|
|
self.mineCoords.append([i,n])
|
|
|
|
self.numberise(board)
|
|
for i in xrange(width):
|
|
for n in xrange(height):
|
|
minecraftAddBlock(i, n, 1, board[i][n])
|
|
|
|
return board
|
|
|
|
def visibleScreen(self):
|
|
board = {}
|
|
for i in xrange(0, width):
|
|
board[i] = {}
|
|
for n in xrange(0, height):
|
|
board[i][n] = " "
|
|
return board
|
|
|
|
def minecraftAddBlock(X, Y, Z, mineAttribute):
|
|
# This equates values passed through mineAttribute with the actual
|
|
# block IDs from Minecraft.
|
|
# 0 is Air, 5 is Wood Planks, 4 is cobblestone, coal is 16
|
|
# Iron is 15, Gold is 14, Diamond is 56, Gold Block is 41,
|
|
# Diamond Block is 57
|
|
mineDict = {"dirt":3, "*":46, " ":20, 0:0, "1":5, "2":4, "3":16, "4":15, "5":14, "6":56, "7":41, "8":57}
|
|
mc.setBlock(X, Y, Z, mineDict[mineAttribute])
|
|
|
|
def explore(x, y, Z): # Z is capitalised because it doesn't
|
|
# need to be changed by the function.
|
|
""" This is the bit that happens when you click on a blank square
|
|
First it checks the squares directly around the clicked square
|
|
If the square it checks is a number, it will display it, and
|
|
if the square it checks is blank, it will add the blank square's
|
|
coordinates to a list or dictionary, then it will keep doing the
|
|
same process to all the coordinates in the list, deleting squares
|
|
that have been checked, and adding new squares, until the list is
|
|
empty. At that point, the area around the original square will be
|
|
revealed, as you would expect to happen in minesweeper."""
|
|
|
|
checked = [[x,y]] # Has been checked and contains either a number or ' '
|
|
toBeCentre = [[x, y]] # Each point in this list will be checked on all sides for the above conditions
|
|
centred = [] # These points have already been checked on all sides
|
|
global cleared
|
|
cleared = []
|
|
|
|
while len(toBeCentre) > 0:
|
|
X, Y = toBeCentre.pop(0)
|
|
centred.append([X,Y])
|
|
minecraftAddBlock(X, Y, Z, 0)
|
|
if [X,Y] not in cleared:
|
|
cleared.append([X,Y])
|
|
for i in xrange(-1, 2):
|
|
for n in xrange(-1, 2):
|
|
|
|
# When I was writing this section, it wouldn't work, and wouldn't work
|
|
# and then after changing it around a million times, suddenly it started working...
|
|
# The only problem is that I don't actually know what I did to make it work... =P
|
|
try:
|
|
if ((newBoard[X+i][Y+n] in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'])and ([X+i, Y+n] not in checked)):
|
|
minecraftAddBlock(X+i, Y+n, Z, 0) #newBoard[X+i][Y+n])
|
|
checked.append([X+i, Y+n])
|
|
if [X+i,Y+n] not in cleared:
|
|
cleared.append([X+i,Y+n])
|
|
|
|
elif newBoard[X+i][Y+n] == " ":
|
|
if (([X+i, Y+n] not in checked) and ([X+i, Y+n] not in toBeCentre)):
|
|
toBeCentre.append([X+i, Y+n])
|
|
checked.append([X+i, Y+n])
|
|
except KeyError:
|
|
pass
|
|
|
|
class WinningCheckThread (threading.Thread):
|
|
|
|
def __init__(self, mineCoords, mineNumber, z):
|
|
self.mineCoords = mineCoords
|
|
self.mineNumber = mineNumber
|
|
self.z = z
|
|
threading.Thread.__init__(self)
|
|
|
|
def run(self):
|
|
global running
|
|
running = True
|
|
mc = minecraft.Minecraft.create()
|
|
while running:
|
|
### This is the winning condition... ###
|
|
flagCount = 0
|
|
correctFlagCount = 0
|
|
|
|
for x in xrange(width):
|
|
for y in xrange(height):
|
|
if mc.getBlock(x, y, 0-1) == 50:
|
|
flagCount += 1
|
|
if [x,y] in self.mineCoords:
|
|
correctFlagCount += 1
|
|
|
|
if (self.mineNumber == correctFlagCount) and (self.mineNumber == flagCount):
|
|
for x in xrange(width):
|
|
for y in xrange(height):
|
|
mc.setBlock(x, y, self.z, 20)
|
|
|
|
mc.postToChat("You Win!!!")
|
|
running = False
|
|
sys.exit()
|
|
|
|
class BlockCheckThread(threading.Thread):
|
|
|
|
def __init__(self):
|
|
|
|
threading.Thread.__init__(self)
|
|
def run(self):
|
|
global running
|
|
running = True
|
|
mc = minecraft.Minecraft.create()
|
|
while running:
|
|
|
|
event = mc.events.pollBlockHits()
|
|
|
|
if event:
|
|
# mc.postToChat("Hit detected")
|
|
eventSplit = str(event[0]).split()
|
|
eventSplit = [eventSplit[1][0], eventSplit[2][0], eventSplit[3][0]]
|
|
cursorX, cursorY, cursorZ = eventSplit
|
|
cursorX = int(cursorX)
|
|
cursorY = int(cursorY)
|
|
cursorZ = int(cursorZ)
|
|
if newBoard[cursorX][cursorY] == "*":
|
|
for y in xrange(height):
|
|
for x in xrange(width):
|
|
# This bit of code's dodgy, because it relies on the
|
|
# creation of "newBoard" external to the function...
|
|
mc.setBlock(x, y, z, 0) # (If you hit a mine it clears the board.)
|
|
mc.postToChat("You Lose!")
|
|
running = False
|
|
sys.exit()
|
|
|
|
if newBoard[cursorX][cursorY] in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']:
|
|
#visibleScreen[x][y] = newBoard[x][y]
|
|
mc.setBlock(cursorX, cursorY, cursorZ, 0) # We just remove the top layer.
|
|
|
|
if newBoard[cursorX][cursorY] == " ":
|
|
explore(cursorX, cursorY, cursorZ)
|
|
|
|
#def main():
|
|
global running
|
|
running = True
|
|
mc = minecraft.Minecraft.create(server.address)
|
|
board = board()
|
|
newBoard = board.create()
|
|
visibleScreen = board.visibleScreen()
|
|
|
|
for x in xrange(width):
|
|
for y in xrange(height):
|
|
mc.setBlock(x,y,-1,0)
|
|
|
|
z = 0 # For now... We can make this dynamic later.
|
|
for y in xrange(height):
|
|
for x in xrange(width):
|
|
# This bit of code's dodgy, because it relies on the
|
|
# creation of "visibleScreen" external to the function...
|
|
minecraftAddBlock(x, y, z, "dirt")
|
|
|
|
WinningCheck = WinningCheckThread(board.mineCoords, board.mineNumber, z)
|
|
BlockCheck = BlockCheckThread()
|
|
BlockCheck.daemon
|
|
WinningCheck.start()
|
|
BlockCheck.start()
|
|
|
|
#main() |