├── .gitignore ├── LICENSE ├── README.md ├── project ├── README.md ├── bomb.mp3 ├── main.py └── treasure.jpg └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | #.idea/ 161 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Amirreza Hashemi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Image Decoding: Unveiling Secrets with Minesweeper 2 | 3 | Welcome to the Image Decoding repository! This interactive project implements a unique puzzle-solving approach inspired by the classic game of Minesweeper. You can use this program to unravel hidden messages concealed within images of your choice. 4 | 5 | 6 | https://github.com/Amirrezahmi/Image-Decoding/assets/89692207/c05a9c88-d416-4899-8145-2a2d7155e052 7 | 8 | ## Features 9 | 10 | - Decoding images using Minesweeper-based puzzles 11 | - Dynamic and visually appealing user interface 12 | - Background color transitions for an immersive experience 13 | - Human verification through logical reasoning and strategic thinking 14 | 15 | ## Prerequisites 16 | 17 | To run the Image Decoding program, make sure you have the following installed: 18 | 19 | - Python (version 3.6 or above) 20 | - tkinter package 21 | - PIL package 22 | - pygame package 23 | 24 | 25 | ## Getting Started 26 | 27 | 1. Clone this repository to your local machine. 28 | 29 | ```bash 30 | git clone https://github.com/Amirrezahmi/Image-Decoding.git 31 | ``` 32 | 33 | 2. Navigate to the project directory. 34 | 35 | 36 | ```bash 37 | cd Image-Decoding 38 | ``` 39 | 40 | 3. Install the required dependencies. 41 | 42 | 43 | ```bash 44 | pip install -r requirements.txt 45 | ``` 46 | 47 | 4. Launch the program. 48 | 49 | ```bash 50 | python main.py 51 | ``` 52 | 5. Select your image or press "Default Picture" button to begin the decoding process. 53 | 54 | ## How to Play 55 | 56 | 1. Left Click: Click on a cell with the left mouse button to reveal its content. 57 | - Pay attention to the number displayed on the revealed cell. This number represents the number of adjacent cells containing clues. 58 | - Utilize the clues to strategically identify safe cells and avoid hidden mines. 59 | 2. Right Click: Click on a cell with the right mouse button to mark it with a flag. 60 | - Flagging a cell indicates your suspicion of a mine. 61 | - Use this feature to mark cells you believe may contain mines, allowing you to avoid them during the decoding process. 62 | - $\textbf{Important}$: When you flag a cell correctly, the corresponding part of the image will be decoded, revealing a portion of the hidden message. However, if you flag a cell incorrectly, that part of the image won't decode, emphasizing that blindly flagging all cells won't help you uncover the entire secret message. 63 | 64 |
65 | 20230617_123525_0000 66 |
67 | 68 | 69 | 70 | 3. Decoding Strategy: Combine the information from the revealed numbers and your flagged cells to make informed decisions. 71 | - Deduce the contents of neighboring cells based on the numbers displayed. 72 | - Exercise caution! If you click on a cell containing a mine without flagging it, the game will restart, and you'll need to begin the decoding process again. 73 | 4. First Click: The first click on the game board is always safe, ensuring you have a chance to observe the initial clue and plan your decoding strategy accordingly. 74 | 5. Winning Condition: Uncover all non-mine cells to successfully decode the image and reveal its secret message. 75 | 76 | 77 | ## Human Verification 78 | 79 | Beyond the exciting decoding adventure, this program can also serve as a human verification tool. By requiring users to employ logical reasoning and strategic thinking to decode the image, it ensures that they are real people interacting with the system. 80 | 81 | ## Contributing 82 | 83 | Contributions are welcome! If you have any ideas, suggestions, or bug fixes, please open an issue or submit a pull request. 84 | 85 | 86 | ## License 87 | 88 | This project is licensed under the [MIT License](https://opensource.org/license/mit/). 89 | 90 | 91 | ## Acknowledgments 92 | 93 | The Minesweeper game concept and image decoding inspiration 94 | 95 | 96 | ## Contact 97 | 98 | 99 | For any inquiries or feedback, please contact amirrezahmi2002@gmail.com. 100 | 101 | Enjoy the adventure of image decoding! 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /project/README.md: -------------------------------------------------------------------------------- 1 | This directory contains the main Python code, a sample treasure picture, and the bomb sound. 2 | -------------------------------------------------------------------------------- /project/bomb.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amirrezahmi/Image-Decoding/efe1387d09a872818a8f43391164e7b766e6114c/project/bomb.mp3 -------------------------------------------------------------------------------- /project/main.py: -------------------------------------------------------------------------------- 1 | from tkinter import * 2 | from PIL import Image, ImageTk, ImageFilter 3 | import random 4 | import colorsys 5 | from tkinter.filedialog import askopenfilename 6 | import pygame 7 | 8 | 9 | pygame.mixer.init() 10 | 11 | # Create the window 12 | window = Tk() 13 | window.title("Image Decoding") 14 | window.resizable(False, False) 15 | 16 | # Get the screen width and height 17 | screen_width = window.winfo_screenwidth() 18 | screen_height = window.winfo_screenheight() 19 | 20 | # Set the size and position of the first window 21 | window_width = 512 22 | window_height = 512 23 | window_x = (screen_width - window_width) // 2 24 | window_y = (screen_height - window_height) // 2 25 | window.geometry(f"{window_width}x{window_height}+{window_x}+{window_y}") 26 | 27 | # Define the starting HSL values for background color 28 | hsl = [0, 0.5, 0.3] # Adjust the lightness value (range: 0.0 to 1.0) 29 | 30 | # Define the color transition step size 31 | step = 0.001 32 | 33 | # Function to update the background color 34 | def update_bg_color(): 35 | # Convert HSL to RGB 36 | rgb = colorsys.hls_to_rgb(hsl[0], hsl[2], hsl[1]) 37 | 38 | # Convert RGB to hexadecimal color code 39 | hex_color = f"#{int(rgb[0] * 255):02x}{int(rgb[1] * 255):02x}{int(rgb[2] * 255):02x}" 40 | 41 | # Set the window background color 42 | window.configure(bg=hex_color) 43 | 44 | # Update the HSL values for the next color 45 | hsl[0] += step 46 | hsl[0] %= 1.0 47 | 48 | # Schedule the next color update 49 | window.after(20, update_bg_color) 50 | 51 | # Start the color update loop 52 | update_bg_color() 53 | 54 | # Function to handle button 1 (Default Picture) 55 | def use_default_picture(): 56 | global image 57 | # Load the default picture 58 | image = Image.open("treasure.jpg").convert("RGBA") 59 | image = image.resize((512, 512)) 60 | window.destroy() # Close the window and proceed to the main window 61 | 62 | # Function to handle button 2 (Upload a Picture) 63 | def upload_picture(): 64 | global image 65 | # Open a file dialog to select an image file 66 | filename = askopenfilename(filetypes=[("Image Files", "*.png;*.jpg;*.jpeg;*.gif")]) 67 | if filename: 68 | # Load the selected image 69 | image = Image.open(filename).convert("RGBA") 70 | image = image.resize((512, 512)) 71 | window.destroy() # Close the window and proceed to the main window 72 | 73 | # Create the buttons in the window 74 | default_button = Button(window, text="Default Picture", command=use_default_picture) 75 | default_button.pack(pady=10) 76 | upload_button = Button(window, text="Upload a Picture", command=upload_picture) 77 | upload_button.pack(pady=10) 78 | 79 | # Start the main loop 80 | window.mainloop() 81 | 82 | # After the window is closed, continue with the main program 83 | 84 | # Load the image and create the mask 85 | blurred_image = image.filter(ImageFilter.GaussianBlur(radius=20)) 86 | 87 | # Create the main window 88 | window = Tk() 89 | window.title("Image decoding") 90 | window.resizable(False, False) 91 | # Define the starting HSL values 92 | hsl = [0, 0.5, 0.3] # Adjust the lightness value (range: 0.0 to 1.0) 93 | 94 | # Define the color transition step size 95 | step = 0.001 96 | 97 | # Function to update the background color 98 | def update_bg_color(): 99 | # Convert HSL to RGB 100 | rgb = colorsys.hls_to_rgb(hsl[0], hsl[2], hsl[1]) 101 | 102 | # Convert RGB to hexadecimal color code 103 | hex_color = f"#{int(rgb[0] * 255):02x}{int(rgb[1] * 255):02x}{int(rgb[2] * 255):02x}" 104 | 105 | # Set the window background color 106 | window.configure(bg=hex_color) 107 | 108 | # Update the HSL values for the next color 109 | hsl[0] += step 110 | hsl[0] %= 1.0 111 | 112 | # Schedule the next color update 113 | window.after(20, update_bg_color) 114 | 115 | # Start the color update loop 116 | update_bg_color() 117 | screen_width = window.winfo_screenwidth() 118 | screen_height = window.winfo_screenheight() 119 | 120 | # Set the window size to cover the entire screen 121 | window.geometry(f"{screen_width}x{screen_height}") 122 | left_frame = Frame(window) 123 | right_frame = Frame(window) 124 | left_frame.pack(side=LEFT) 125 | right_frame.pack(side=RIGHT) 126 | canvas = Canvas(right_frame, width=image.width, height=image.height) 127 | canvas.pack() 128 | 129 | # Calculate the number of cells in the game board based on the size of the image 130 | cell_size = 30 131 | num_cells_x = image.width // cell_size 132 | num_cells_y = image.height // cell_size 133 | 134 | # Create the game board buttons 135 | buttons = [[None for _ in range(num_cells_x)] for _ in range(num_cells_y)] 136 | 137 | first_click = True 138 | def start_game(): 139 | global image_tk, mask, board, mines, first_click 140 | 141 | # Create the mask 142 | mask = Image.new("L", image.size, 0) 143 | 144 | # Display the image on the canvas 145 | image_tk = ImageTk.PhotoImage(blurred_image) 146 | canvas.create_image(0, 0, anchor=NW, image=image_tk) 147 | 148 | # Create the game board 149 | board = [[0 for _ in range(num_cells_x)] for _ in range(num_cells_y)] 150 | mines = random.sample([(i, j) for i in range(num_cells_x) for j in range(num_cells_y)], 10) 151 | 152 | # Move a mine from the randomly selected cell to a different cell 153 | if first_click: 154 | random_cell = random.choice(mines) 155 | new_cell = random.choice([(i, j) for i in range(num_cells_x) for j in range(num_cells_y) if (i, j) not in mines]) 156 | mines.remove(random_cell) 157 | mines.append(new_cell) 158 | 159 | for mine in mines: 160 | board[mine[0]][mine[1]] = -1 161 | 162 | # Reset the game board buttons 163 | for i in range(num_cells_x): 164 | for j in range(num_cells_y): 165 | button = Button(window, text=" ") 166 | button.bind("", lambda event, i=i, j=j: open_cell(i, j)) 167 | button.bind("", lambda event, i=i, j=j: flag_cell(i, j)) 168 | button.place(x=i * cell_size, y=j * cell_size, width=cell_size, height=cell_size) 169 | buttons[i][j] = button 170 | 171 | first_click = True 172 | 173 | 174 | 175 | def open_cell(i, j): 176 | global first_click 177 | 178 | # If the cell is flagged, do nothing 179 | if buttons[i][j]['text'] == "🚩": 180 | return 181 | 182 | # If the cell is flagged or a mine (and not the first click), game over 183 | if board[i][j] == -1 and not first_click: 184 | # Display the bomb symbol and play the bomb sound 185 | buttons[i][j].config(text="💣", fg="red") 186 | pygame.mixer.music.load("bomb.mp3") 187 | pygame.mixer.music.play() 188 | 189 | # Restart the puzzle 190 | start_game() 191 | return 192 | 193 | if buttons[i][j]['state'] == DISABLED or (board[i][j] == -1 and not first_click): 194 | return 195 | 196 | if first_click: 197 | first_click = False 198 | # Ensure the first click is not a mine 199 | while board[i][j] == -1: 200 | start_game() 201 | # Open some cells around the first click 202 | for x in range(max(0, i - 1), min(num_cells_x, i + 2)): 203 | for y in range(max(0, j - 1), min(num_cells_y, j + 2)): 204 | open_cell(x, y) 205 | 206 | # Calculate the number of adjacent mines 207 | num_mines = sum(board[x][y] == -1 for x in range(max(0, i - 1), min(num_cells_x, i + 2)) for y in 208 | range(max(0, j - 1), min(num_cells_y, j + 2))) 209 | 210 | # Update the button text and color 211 | buttons[i][j].config(text=str(num_mines), 212 | fg="red" if num_mines == 1 else "blue" if num_mines == 2 else "purple" if num_mines == 3 else "black") 213 | 214 | # Update the mask and blend it with the original image 215 | mask.paste(255, (i * cell_size, j * cell_size, (i + 1) * cell_size, (j + 1) * cell_size)) 216 | revealed_image = Image.composite(image, blurred_image, mask) 217 | 218 | # Update the image on the canvas 219 | global image_tk 220 | image_tk = ImageTk.PhotoImage(revealed_image) 221 | canvas.create_image(0, 0, anchor=NW, image=image_tk) 222 | 223 | def flag_cell(i, j): 224 | # If the cell is already opened or flagged, do nothing 225 | if buttons[i][j]['text'] != " ": 226 | return 227 | 228 | # Flag the cell 229 | buttons[i][j].config(text="🚩", state=DISABLED) 230 | 231 | # Check if the flagged cell is a bomb 232 | if (i, j) in mines: 233 | # Update the mask and blend it with the original image 234 | mask.paste(255, (i * cell_size, j * cell_size, (i + 1) * cell_size, (j + 1) * cell_size)) 235 | revealed_image = Image.composite(image, blurred_image, mask) 236 | 237 | # Update the image on the canvas 238 | global image_tk 239 | image_tk = ImageTk.PhotoImage(revealed_image) 240 | canvas.create_image(0, 0, anchor=NW, image=image_tk) 241 | 242 | 243 | start_game() 244 | window.mainloop() -------------------------------------------------------------------------------- /project/treasure.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amirrezahmi/Image-Decoding/efe1387d09a872818a8f43391164e7b766e6114c/project/treasure.jpg -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pygame==2.0.1 2 | Pillow==9.5.0 3 | tkinter==8.6 --------------------------------------------------------------------------------