Metadata-Version: 2.4
Name: metagrid
Version: 0.5.0
Summary: Metagrid is a simple game engine for 2D Grid Games
Project-URL: Homepage, https://github.com/MMarchand-NSI/metagrid
Project-URL: Repository, https://github.com/MMarchand-NSI/metagrid
Project-URL: Issues, https://github.com/MMarchand-NSI/metagrid/issues
Author-email: Matthieu Marchand <matthieu.marchand@gmail.com>
License: License
        =======
        
        Copyright 2025 Matthieu Marchand
        
        Metagrid is licensed under the Apache License, Version 2.0 (the "License");
        you may not use this file except in compliance with the License.
        You may obtain a copy of the License at
        
            http://www.apache.org/licenses/LICENSE-2.0
        
        Unless required by applicable law or agreed to in writing, software
        distributed under the License is distributed on an "AS IS" BASIS,
        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        See the License for the specific language governing permissions and
        limitations under the License.
License-File: LICENSE
License-File: NOTICE
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Python: >=3.10
Requires-Dist: arcade>=3.3.0
Requires-Dist: pillow>=11
Requires-Dist: websockets>=16.0
Provides-Extra: network
Requires-Dist: websockets>=16.0; extra == 'network'
Description-Content-Type: text/markdown

# Metagrid

*[Version française](https://github.com/MMarchand-NSI/metagrid/blob/main/README.fr.md)*

## Description

Metagrid is a Python library for creating simple 2D grid games.
Each cell in the grid can display:

- a solid color
- an image
- a character

## Installation

```shell
pip install metagrid
```

---

## Tutorial

### Program structure

Every metagrid program follows the same three-step pattern:

1. Declare the game state variables (globals).
2. Write the *callback* functions (`on_init`, `on_update`, `on_draw`, `on_click`, `on_key`).
3. In the `if __name__ == "__main__"` block: create the engine, register the callbacks, then start the loop with `game.start()`.

---

### 1. Create the engine

```python
import metagrid

game = metagrid.create(rows, cols, cell_size, margin)
```

| Parameter   | Role                                        |
|-------------|---------------------------------------------|
| `rows`      | Number of rows in the grid                  |
| `cols`      | Number of columns in the grid               |
| `cell_size` | Size in pixels of each cell                 |
| `margin`    | Thickness in pixels of the border between cells |

---

### 2. Callbacks

Callbacks are plain functions passed to the engine. Metagrid calls them automatically at the right time.

#### `on_init` — initialization

Called **once** at startup, before the first frame. Reset all game state here.

```python
def init():
    print("Game initialized")

game.on_init(init)
```

#### `on_update` — game logic

Called **every frame**, before drawing. Advance the game state here.

`game.frame_no` holds the current frame number (starts at 0, increments by 1 per frame). Useful for triggering periodic actions without an external timer.

```python
def update():
    if game.frame_no % 120 == 0:   # every 2 seconds at 60 fps
        print("tick")

game.on_update(update)
```

#### `on_draw` — drawing

Called **every frame**, after `update`. Translate the game state into colors / images / characters here.

```python
def draw():
    for i in range(5):
        for j in range(5):
            val = grid[i][j]
            if val == 1:
                game.set_cell_color(i, j, "#135683")
            elif val == 2:
                game.set_cell_char(i, j, "X", "#000000")

game.on_draw(draw)
```

#### `on_click` — mouse click

Called when the user clicks on a cell.

```python
def click(i: int, j: int, button: str):
    # i, j   : coordinates of the clicked cell
    # button : "left", "right" or "middle"
    print(f"Cell ({i}, {j}) clicked with {button}")

game.on_click(click)
```

#### `on_key` — keyboard

Called when the user presses a key.

```python
def key(k: str):
    # k : character ('a', 'z', ' ', …) or special key name
    print(f"Key {k} pressed")

game.on_key(key)
```

---

### 3. Drawing in cells

Three functions change the appearance of cell `(i, j)` (row `i`, column `j`, both starting at 0 from the top-left):

#### Solid color

```python
game.set_cell_color(i, j, "#RRGGBB")
```

#### Character

```python
game.set_cell_char(i, j, "A", "#RRGGBB")
```

One character per cell, centered on top of the background.

#### Image

Load an image once (before `game.start()`), then display it:

```python
game.load_image("image_name", "path/to/image.png")
# …
game.set_cell_image(i, j, "image_name")
```

---

### 4. Start the loop

```python
game.start()   # blocks until the window is closed
```

---

### Full example

```python
import metagrid
from metagrid import AbstractEngine

grid: list[list[int]] = [
    [0, 1, 0, 0, 0],
    [0, 0, 2, 1, 0],
    [2, 0, 0, 0, 0],
    [0, 1, 0, 0, 0],
    [0, 0, 0, 0, 1],
]

game: AbstractEngine


def init():
    print("Game initialized")


def click(i: int, j: int, button: str):
    print(f"Cell ({i}, {j}) clicked with {button}")


def key(k: str):
    print(f"Key {k} pressed")


def update():
    if game.frame_no % 120 == 0:
        print("Update every 2 seconds")


def draw():
    for i in range(5):
        for j in range(5):
            val = grid[i][j]
            if val == 1:
                game.set_cell_color(i, j, "#135683")
            elif val == 2:
                game.set_cell_char(i, j, "X", "#000000")


if __name__ == "__main__":
    game = metagrid.create(5, 5, 50, 1)

    game.on_init(init)
    game.on_click(click)
    game.on_key(key)
    game.on_update(update)
    game.on_draw(draw)

    game.start()
```

The full source is available in [`examples/full_example.py`](examples/full_example.py).

---

## Included examples

| File                                 | Game         |
|--------------------------------------|--------------|
| [`examples/snake.py`](examples/snake.py) | Snake |
| [`examples/jeudelavie.py`](examples/jeudelavie.py) | Game of Life |
| [`examples/jeu2048.py`](examples/jeu2048.py) | 2048 |
| [`examples/puissance4.py`](examples/puissance4.py) | Connect Four |
| [`examples/memory.py`](examples/memory.py) | Memory |
| [`examples/lights_out.py`](examples/lights_out.py) | Lights Out |
| [`examples/sokoban.py`](examples/sokoban.py) | Sokoban |
| [`examples/taquin.py`](examples/taquin.py) | Sliding Puzzle |
| [`examples/wordle.py`](examples/wordle.py) | Wordle |
