# Mine sweeper 
#    30 May 17 - Use F not D for flagging
#              - Display boards of varying size
#

import random

# Constants
#   The size of the mine field can be varied but >10
#   is not well formatted
MINEVALUE = -2
MINECLEAR = -1
SIZE = 6

# Create the mine field, with mines in approx 10%
# of locations hidden randomly.
#  * The mine field is stored as a 2-D array
#  * Initially, all entries are negative - clear or mined
#  * When a location is tested, the entry is the number of 
#      mined neighbours
def makeMineField():
    mines = []
    for x in range(0, SIZE):
        m = []
        for y in range(0, SIZE):
            if random.random() > 0.9:
                m.append(MINEVALUE)
            else:
                m.append(MINECLEAR)
        mines.append(m)
    return mines

# Display the column headers
def showHeader():
    line = "    "
    col = 0
    while col < SIZE:
        line = line + str(col) + " "
        col += 1
    print(line)
    print("   ", "--" * SIZE)

# Display the curent state of the mine field 
#   * mines - the mine field
#   * detects - the list of flagged locations
# Locations yet to be tested are shown as '*'
# Flagged locations - assumed to be correct - are
#   as 'M'
def printMines(mines, detects):
    showHeader()
    row = 0
    for ms in mines:
        line = " " + str(row) + ": "
        col = 0
        for m in ms:
            if (col, row) in detects:
                line = line + "M "
            elif m < 0:
                line = line + "* "
            else:
                line = line + str(m) + " "
            col = col + 1
        print(line)
        row = row + 1

# Reveal the true state of the mine field at the end of the game:
#   - clear cells                 .
#   - uncleared cells             *
#   - correctly detected mines    m
#   - undetected mines            M
#   - incorrect mines             x
def showMines(mines, detects):
    showHeader()
    row = 0
    complete = True
    for ms in mines:
        col = 0
        line = " " + str(row) + ": "
        for m in ms:
            if m == MINEVALUE and (col, row) in detects:
                line = line + "m "
            elif m == MINEVALUE:
                line = line + "M "
                complete = False
            elif m >= 0:
                line = line + ". "
            elif (col, row) in detects:
                line = line + "x "
                complete = False
            else:
                line = line + "* "
                complete = False
            col = col + 1
        print(line)
        row = row + 1
    return complete

# Count the number of nighbouring locations containing a mine
def scoreNeighbours(mines, c, r):
    sum = 0
    sum = sum + scoreCell(mines, c-1, r-1)
    sum = sum + scoreCell(mines, c-1, r)
    sum = sum + scoreCell(mines, c-1, r+1)
    sum = sum + scoreCell(mines, c, r-1)
    sum = sum + scoreCell(mines, c, r+1)
    sum = sum + scoreCell(mines, c+1, r-1)
    sum = sum + scoreCell(mines, c+1, r)
    sum = sum + scoreCell(mines, c+1, r+1)
    return sum

# Count the mines (0 or 1) in one cell, giving
#   zero for locations outside the mine field
def scoreCell(mines, c, r):
    if r < 0 or r >= len(mines):
        return 0
    m = mines[r]
    if c < 0 or c >= len(m):
        return 0
    if m[c] == MINEVALUE:
        return 1
    return 0

# Take a turn
#   * Prompt the player to F or T
#   * If T (test), end game if mine exploded or update 
#       mine field with score
#   * If F (flag), add to list of detects (whether correct or not)
# Return true if still alive
def turn(mines, detects):
    dt = ""
    while dt not in ['F', 'T']:
        dt = input("Flag(F) or Test(T)? ")
    
    col = int(input("Which column> "))
    row = int(input("Which row> "))

    if dt == 'T':
        if mines[row][col] == MINEVALUE:
            print("B O O O O M")
            return False

        mines[row][col] = scoreNeighbours(mines, col, row)
        return True
    else:
        detects.append((col,row))
        return True

# Data Structures
#    The mine field is a 2-D array
#    The flagged locations ar stoed as a list of pairs 
ms = makeMineField()
detects = []
turnNum = 0

# MAIN Program
# ------------
#  Takes turns until a mine explodes or the board is cleared
#  Reveal the mine field 
#
#showMines(ms, detects) # uncomment this to cheat
alive = True
while alive and turnNum < SIZE * SIZE:
    printMines(ms, detects)
    alive = turn(ms, detects)
    turnNum = turnNum + 1
    
if showMines(ms, detects):
    print("Well done")
else:
    print("Try again!")
