# How to make a memory game using tkinter.

Memory game are fun to play and easy to make.   
Here's how I made mine using tkinter in python.

### How the game will be like

- There will be some buttons with numbers from 0 to a number n-1
- There are 25 chances
- On clicking, the buttons reveal their numbers
- When two buttons are clicked, if their numbers are same, they become disabled and a label shows that it is a match
- Else, a messagebox shows that it is incorrect and both numbers disappear
- When all the numbers are correct it shows you won
- When all the chances are over it shows you lose
- There is a menu bar with the options to quit game and reset game


![Completed.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1649155585122/C5_WsRkUW.png)
*A completed game*


![Wrong.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1649155690199/MP_izEokt.png)
*When The player gets it wrong*

### How the game works

- When a tile is clicked, its text is changed to the corresponding number in a list of `matches`
- The program stores this tile's index in a list called `answer_list`
- When a second tile is clicked, the following happens:
     - The program adds the second tile's index also to the `answer_list`
     - The program checks if both tiles' corresponding values in `matches` are equal
     - Depending on whether they are equal or not, do the respective
     - Empty the `answer_list` as two buttons have been clicked

### Starting with the window

All this code is the same for most tkinter projects


```python
from tkinter import *
from tkinter import messagebox

root = Tk()
root.mainloop() #This line is always at the end of the program
``` 
### Defining the global variables
```
won = False
count = 0
answer_list = []
```

### Making a list of all the `matches`
Here we will use a variable called `NUMBER` which stores the number of unique tiles   
Using list comprehension, we will make a list of all the `matches` twice (because there have to be two of the same number) and the shuffle the list

```python
from random import shuffle

NUMBER = 10 #We will be having ten unique tiles for this example but you can change this
matches = [x for x in range(NUMBER) for _ in range(2)]
shuffle(matches)
```

### Creating the `tiles` and adding them to the screen

First a frame for all the tiles to be contained in 
```
my_frame = Frame(root) #Frame for all the tiles
my_frame.pack()
```
 Then, a list containing all the tiles
```
tiles = []
    for i in range(len(matches)):
        tiles.append(Button(
            my_frame,
            text = ' ', 
            font = ("Helvetica", 40),
            height =1, width = 3,
            command = lambda i=i: onclick(i))) #The onclick function will be shown later
```

Now comes the tricky part. We need to arrange the tiles in such a way that the columns and rows are factors of the total number of tiles. I have made a function to do that. This will work for any composite number of tiles, and since our number of tiles are double of the unique tiles, we know that it will always be even (and thus by extension composite).

```python
from math import sqrt

def find_rows_cols(number):
    max_cols = int(sqrt(number))
    for i in range(max_cols, 0, -1):
        if number % i == 0:
            cols = i
            break
    rows = int(number/cols)
    return rows, cols
```
Now, we add the `tiles` to the `my_frame` we made before
```
rows, cols = find_rows_cols(len(tiles))
#Adding the tiles to screen
for i,tile in enumerate(tiles):
    row = i%cols
    col = int(i/cols)
    tile.grid(row= row, column = col)
```

### Defining the `onclick` function
First, We need to define a label in which we will show whether it was wrong or right. By default it shows how many chances you have left

```
label = Label(root, text= f"{chances = }", font= ("Helvetica", 30))
label.pack()
```

To change the text of the tile when it is clicked

```python
def onclick(index):
    global answer_list, label, won, NUMBER, tiles, chances, tiles
    label["text"] = f'{chances = }'

    if tiles[index]["text"] == ' ': #If the tile has been clicked 
        tiles[index]["text"] = matches[index]
        answer_list.append(index)

    else: #If it has not been clicked
        tiles[index]["text"] = ' '
        answer_list = []
```

Now, we have to get to the points part.   
The following code occurs when two tiles have been clicked and both those tiles' corresponding items in the matches list are equal. 

```python

    if len(answer_list) == 2: # If two tiles have been clicked
        if matches[answer_list[0]] == \
            matches[answer_list[1]]:
            tiles[answer_list[0]]["state"] = DISABLED #Disabling both buttons
            tiles[answer_list[1]]["state"] = DISABLED
            label["text"] = f"It's a Match! {chances = }"
            won += 1

            if won == NUMBER:
                label["text"] = "You Won!"

```

Now, we even need to add in the code for what happens when they are not equal

```
else:
            chances -= 1
            label["text"] = f"Ahhh try again {chances = }"
            messagebox.showinfo("Incorrect", "Incorrect")

            tiles[answer_list[0]]["text"] = ' '
            tiles[answer_list[1]]["text"] = ' '
```

Emptying the `answer_list`

```
answer_list = []
```

Finally, to add what happens when the `chances` become zero
```
if chances == 0:
        for tile in tiles:
            tile["state"] = DISABLED
        label["text"] = "You Lost D:"
```

After all this, our `onclick` function is huge. Here is the complete code for the onclick funtion
```
def onclick(index):
    global answer_list, label, won, NUMBER, tiles, chances, tiles
    label["text"] = f'{chances = }'

    if tiles[index]["text"] == ' ': #If the tile has been clicked 
        tiles[index]["text"] = matches[index]
        answer_list.append(index)

    else: #If it has not been clicked
        tiles[index]["text"] = ' '
        answer_list = []

    if len(answer_list) == 2: # If two tiles have been clicked
        if matches[answer_list[0]] == \
            matches[answer_list[1]]:
            tiles[answer_list[0]]["state"] = DISABLED
            tiles[answer_list[1]]["state"] = DISABLED
            label["text"] = f"It's a Match! {chances = }"
            won += 1
            if won == NUMBER:
                label["text"] = "You Won!"
        else:
            chances -= 1
            label["text"] = f"Ahhh try again {chances = }"
            messagebox.showinfo("Incorrect", "Incorrect")

            tiles[answer_list[0]]["text"] = ' '
            tiles[answer_list[1]]["text"] = ' '

        answer_list = []

    if chances == 0:
        for tile in tiles:
            tile["state"] = DISABLED
        label["text"] = "You Lost D:"
```

### Adding a options menu

```python

my_menu = Menu(root)
root.config(menu=my_menu)

# Create an Options Dropdown Menu
option_menu = Menu(my_menu, tearoff=False)
my_menu.add_cascade(label="Options", menu=option_menu)
option_menu.add_command(label="Reset Game", command=reset)
option_menu.add_separator()
option_menu.add_command(label="Exit Game", command=root.quit)
```

### Finally, to define the reset function for the board to be reset when the menu resets
```python
def reset():
    global matches, won, tiles, chances
    chances = 25
    won = False
    shuffle(matches)

    #Reset Label
    label["text"] = ' '

    tiles = [] #Making the `tiles` list from scratch
    for i in range(len(matches)): 
        tiles.append(Button(my_frame, text = ' ', 
            font = ("Helvetica", 40), height =1, width = 3,
            command = lambda i=i: onclick(i)))

    rows, cols = find_rows_cols(len(tiles))
    for i in range(len(tiles)): #Positioning the tiles the same way we did before
        row = i%cols
        col = int(i/cols)
        tiles[i].grid(row= row, column = col)
```

Hooray! You have made the game.   
Entire code for game: %[https://github.com/VasTheCoder/Miscellaneous-Projects/blob/main/memory.py]
