Home > Mobile >  How do I use nested lists properly in that context
How do I use nested lists properly in that context

Time:11-27

I need to create a memory game in Python. I need to print a 5x4 (4 rows, 5 elements in a line) field in the console and the fields should have names like a1, b1, c1... in the next row a2, b2, c2 etc. We've already got a list of symbols, which should be used in the game (list1). One of the instructions we have is to create nested lists here, which gives me a hard time. If you see below, I created a nested list, which shuffles the cards and puts them in a list. When I print those, I get the 5x4 row as needed. If an user writes a1 and b2 in the console, I have to print the exact symbols that are placed in that row.

However, I have no idea how I should proceed at this point. I had some thoughts, but they seem to be unprofessional...

My idea:

  • Create new list with fields a1, b1, etc. and print those. If an user writes a1 and b1, I get the index in the list and find the symbol at the same index in my cards list. --> Nested lists seem to be useless with that idea creating a new list with fields is probably not wanted in that task
cards = ["✿", "❄", "★", "♥", "✉", "✂", "✖", "✈", "♫", "☀",
         "✿", "❄", "★", "♥", "✉", "✂", "✖", "✈", "♫", "☀"]

My solution so far:

def create_grid(cards):

    random.shuffle(cards)
    cards2 = [cards[i:i 5] for i in range(0, len(cards), 5)]
    return cards2   

CodePudding user response:

As the names are fixed to a1 to d5, you can use the ASCII code using ord for the first letter to get the row index.

import random

cards = ["✿", "❄", "★", "♥", "✉", "✂", "✖", "✈", "♫", "☀",
         "✿", "❄", "★", "♥", "✉", "✂", "✖", "✈", "♫", "☀"]


def create_grid(cards):
    shuffled_cards = cards.copy()
    random.shuffle(shuffled_cards)
    cards2 = [shuffled_cards[i:i   5] for i in
              range(0, len(shuffled_cards), 5)]
    return cards2


grid = create_grid(cards)
print("Grid:")
for line in grid:
    print(" ".join(line))

print("")

while True:
    field = input("Enter a name of the field (0 to exit): ")
    if field[0] == '0':
        break
    field = field.lower()
    row = ord(field[0]) - ord('a')
    column = int(field[1]) - 1
    if row >= len(grid) or column >= len(grid[0]):
        print("The field is out of grid size. Try again.\n")
        continue
    print(f"{field}: {grid[row][column]}\n")

Output:

Grid:
✂ ✂ ❄ ☀ ✉
✿ ✖ ✈ ★ ♫
✖ ✉ ♥ ✿ ❄
♫ ★ ✈ ♥ ☀

Enter a name of the field (0 to exit): a2
a2: ✂

Enter a name of the field (0 to exit): b1
b1: ✿

Enter a name of the field (0 to exit): d4
d4: ♥

Enter a name of the field (0 to exit): e2
The field is out of grid size. Try again.

Enter a name of the field (0 to exit): 0

Explanation:

  • I did not modify the original cards list. Rather I copied the content and shuffled them on the copy: shuffled_cards = cards.copy()
  • The program takes field name from the user from range a1 to d4 and then print the value of that index from the grid.
  • I parsed the input from the user and calculated the row and column. Row is calculated by using the ASCII code of the first character and column is calculated by subtracting 1 from the second character of user input as the list is 0 indexed.
  • The grid has a size of 4 x 5. Thus, if user enters some large input, it will tell the user that the input field is our of the grid size.
  • It takes field name from user until user enters 0. You can change it as your need.

CodePudding user response:

Instead of using fields, translate the reference to two indices; one for the row, the other the column. Your nested list appears to use a list of rows, and each nested list is a list of cards at each column. It could be that your nested list actually encodes columns and that each nested list holds cards at each row, but then you only have to swap the row and col variables in one place.

Once you have separate row and col variables you can then use those two values to index the nested list:

col, row = interpret_reference(reference)
card = grid[row][col]

where reference is the user's input. Note that grid[row] gives you the inner list of columns, and grid[row][col] then picks the right column.

Python lists are indexed with non-negative integers, so numbers from 0 and up.

So you need a way to translate the letter (a, b, c, d) to an integer column index starting at 0, and the number (1, 2, 3, 4, 5) to an integer value one lower, so "1" becomes 0, etc., an operation defined as a function named interpret_reference().

There are lots of ways of doing that. One simple way would be to use a dictionary for both:

col_letters = {"a": 0, "b": 1, "c": 2, "d": 3, "e": 4}
row_numbers = {"1", 0, "2": 1, "3": 2, "4": 3}

def interpret_reference(userinput):
    col, row = userinput
    return col_letters[col], row_numbers[row]

Note that this returns col, row, the same order as the user input. In my first example code snippet, the variables are then used in the swapped order to index the nested list. If your actual nested list structure is swapped, simply swap the order to grid[col][row].

The interpret_reference() function ignores error handling (where a user enters something that's not a valid reference). The function will raise an exception in that case, either ValueError when the input is not exactly 2 characters, or KeyError when they used a character that's not a valid row or column reference. See Asking the user for input until they give a valid response for methods of handling user input with error handling.

There are, as mentioned, other techniques to map the user input to integer numbers.

  • Computers encode text as numbers. What numbers exactly doesn't really matter, but you can rely on the numbers for lower-case ASCII letters (a, b, c, d, etc.) to be consecutive. The ord() function gives you the integer number that the Unicode standard has assigned to a character, and if you then subtract the Unicode number (a codepoint) for the character "a" from that value you get an number starting at 0, provided you started with a lowercase letter:

    col = ord(col_reference) - ord("a")
    
  • The int() type can take a string and interpret it as an integer number. Given the string "1", it returns the integer value 1. Subtract 1 and you are done:

    row = int(row_reference) - 1
    

This changes how you handle errors, however; using dictionaries makes it easier to detect incorrect input, but using ord() and int() means you also then have to validate that the resulting integers are not negative numbers and not too large (so larger than 3 or 4).

Side note: your create_grid function changes the cards list in-place. It is probably fine, as long as your other code doesn't expect the card symbols to stay in their original order. You can replace random.shuffle(cards) with shuffled = random.sample(cards, len(cards)) to get a new list that is shuffled without affecting the original cards list.

  • Related