pythonlistdictionarycolorama

Print an item in a list in a color based on the item


I've written a script for a very clunky version of minesweeper.

The player selects the square they want to reveal based on co-ordinates (like in chess) and the board is updated with the values showing how many mines are in the squares around the chosen co-ordinates. The player can also flag where they think a mine is, which shows up as an "f".

How the player board looks after the player has selected a few squares

Unfortunately this is unreadable which makes it very slow going. So I was hoping to update the code with some colour, where all "2"s are printed in blue, all "3"s in red etc. I have all the components; I've downloaded colorama but I can't get it to work. I'm stuck with the rest.

Code that prints the initial empty grid:

difficulty_dictionaries = {
    1 : {
        "difficulty": "Beginner",
        "x_axis": 8, 
        "y_axis": 8, 
        "mine_no": 10
    },
    2 : {
        "difficulty": "Intermediate",
        "x_axis": 16, 
        "y_axis": 16, 
        "mine_no": 40
    },
    3 : {
        "difficulty": "Expert",
        "x_axis": 26, 
        "y_axis": 16, 
        "mine_no": 98
    }
}   

ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

def set_difficulty():
    print ("Please select difficulty level by entering either 1, 2 or 3 based on the corresponding difficulty")
    diff_input = input ("1. Beginner: \n2. Intermediate: \n3. Expert: ") 
    
    return difficulty_dictionaries[int(diff_input)]

def set_user_board():
    grid = []
    grid.append("  ")
    for set_x in range(diff_dict["x_axis"]):
        grid.append(ALPHABET[set_x])
    grid[0] = (" ".join(grid))

    del grid[1:]
    grid.append("  " + "+" + ("-+")*(diff_dict["x_axis"]))
    for set_y in range(diff_dict["y_axis"]):
        grid.append(str(set_y + 1) + " |" + (" " + "|")*diff_dict["x_axis"]) if set_y < 9 else grid.append(str(set_y + 1) + "|" + (" " + "|")*diff_dict["x_axis"])
        grid.append("  +" + ("-+")*diff_dict["x_axis"])
    return(grid)

def print_grid():
    print('\n'.join(' '.join(map(str, b)) for b in grid))
    return

diff_dict = set_difficulty()
grid = set_user_board()
print_grid()

Output:

Empty player grid

As squares are selected, the values are taken from another reference list of lists and inserted into grid at the relevant point.

I stole the part of the code that prints the grid from another answer on SO but I don't know how it works. I have also tried this:

def print_grid():
    for x in grid:
        print(*x)
    return

which is far more understandable. But when I try to apply the colour to the items in the list I'm encountering some problems - mostly with the *.

def print_grid():
    for x in grid:
        print(f"{Fore.RED}" + x)

    return

That prints an all red grid - but it's squished: Squished, red grid


Solution

  • First explanations:

    You have 2D grid and x can means row with many values -

    When you use print(*x) then it runs it as print(x[0], x[1], ...) so it prints many separated elements and print() automatically add space between elements. You could say that every comma , adds space in output.

    Try print("A", "B") and you get "A B" instead of "AB"

    When you print(f"{Fore.RED}" + x) then you print one string (probably row converted to string) and print() doesn't know that it needs spaces between elements. You have to format this string with spaces before using print()


    It would be simpler to keep grid as 2D grid (without converting rows to string) and use nested for-loops `(with better names for variables)

    for row in grid:
        for item in row:
            print(item, end=' ')
        print() # go to next line
    

    And now you can use if/else to print it in different colors

    for row in grid:
        for item in row:
            if item in ('+', '-', '|'):  # or `if item in "+-|"`
                color = Fore.RED
            elif item in ('1', '2', '3', '4', '5'): # or `if item in "12345"`
                color = Fore.GREEN
            else: 
                color = Fore.WHITE
            print(f"{color}{item}", end=' ')   # I add `space` instead of `\n` as `end` 
        print() # go to next line
    

    Minimal working example:

    from colorama import Fore, Back, Style
    
    grid = [
        ['+', '-', '+', '-', '+', '-', '+'],
        ['|', '1', '|', ' ', '|', ' ', '|'],
        ['+', '-', '+', '-', '+', '-', '+'],
        ['|', 'X', '|', '2', '|', ' ', '|'],
        ['+', '-', '+', '-', '+', '-', '+'],
        ['|', ' ', '|', 'O', '|', '3', '|'],
        ['+', '-', '+', '-', '+', '-', '+'],
    ]
    
    # ---
    
    # letters
    
    print(end='   ')
    for item in 'ABC':
        print(f" {Fore.WHITE + Style.BRIGHT}{item} ", end=' ')
    print()
    
    # rows 
    
    for number, row in enumerate(grid, 1):
        
        # row's number
        
        if number % 2 == 1:
            print("  ", end='')
        else:
            print(f"{Fore.WHITE + Style.BRIGHT}{number//2} ", end='')
    
        # row's grid
        for item in row:
            if item in ('+', '-', '|'):  # or `if item in "+-|"`
                color = Fore.RED + Style.BRIGHT
            elif item in ('1', '2', '3', '4', '5'): # or `if item in "12345"`
                color = Fore.GREEN + Style.BRIGHT
            elif item in ('X', 'O'): # or `if item in "12345"`
                color = Fore.BLUE + Style.BRIGHT
            else: 
                color = Fore.WHITE + Style.BRIGHT
            print(f"{color}{item}", end=' ')   # I add space at the end of `f-string`
        print() # go to next line
    
    

    Result (with colors) in console:

    enter image description here


    EDIT:

    I made more universal version which use grid without lines +-+

    from colorama import Fore, Back, Style
    import string
    
    grid = [
        ['1', 'D', ' ', 'A', '4'],
        ['E', '2', '5', ' ', 'B'],
        ['6', 'F', '3', 'C', ' '],
    ]
    
    # ---
    
    HEIGTH = len(grid) 
    WIDTH  = len(grid[0])
    
    LETTERS = string.ascii_uppercase[:WIDTH]
    
    # --- columns letters ---
    
    print(end='   ')
    for item in LETTERS:
        print(f' {Fore.WHITE + Style.BRIGHT}{item}  ', end='')
    print()  
    
    # --- rows ---
    
    for number, row in enumerate(grid, 1):
        
        # top line
        
        print(f'{Fore.RED + Style.BRIGHT}  +' + ' - +'*WIDTH)
    
        # row with data
        
        # number
        print(f'{Fore.WHITE + Style.BRIGHT}{number} ', end='')
        
        # first left line
        print(f'{Fore.RED + Style.BRIGHT}| ', end='')
    
        # data with right line
        for item in row:
            if item in '123':
                color = Fore.GREEN + Style.BRIGHT
            elif item in '456':
                color = Fore.YELLOW + Style.BRIGHT
            elif item in 'ABCDEF': # or `if item in "12345"`
                color = Fore.BLUE + Style.BRIGHT
            else: 
                color = Fore.WHITE + Style.BRIGHT
                
            print(f"{color}{item} {Fore.RED + Style.BRIGHT}| ", end='')   # I add space at the end of `f-string`
            
        print() # go to next line
    
    # --- last (bottom) line ---
    
    print(f'{Fore.RED + Style.BRIGHT}  +' + ' - +'*WIDTH)
    

    Result (with colors) in console:

    enter image description here