programming-game


Nameprogramming-game JSON
Version 0.0.17 PyPI version JSON
download
home_pageNone
SummaryA Python client for the Programming Game.
upload_time2025-09-16 16:35:43
maintainerNone
docs_urlNone
authorNone
requires_python>=3.11
licenseMIT
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Programming Game Client

A Python client for the Programming Game. This package allows you to write bots to play the game.

## Installation

To install the package, run the following command:

```bash
pip install programming-game
```

### Database Integration (Optional)

For database logging and analytics, install with database support:

```bash
pip install programming-game[db]
```

Set up your PostgreSQL database with TimescaleDB:

```sql
CREATE DATABASE programming_game;
CREATE EXTENSION IF NOT EXISTS timescaledb;
```

Configure the database URL:

```bash
export DATABASE_URL="postgresql://user:password@localhost/programming_game"
```

## Usage

To run a bot, you need to provide your credentials via environment variables.

```bash
export GAME_CLIENT_ID="YOUR_CLIENT_ID"
export GAME_CLIENT_KEY="YOUR_CLIENT_KEY"
```

Then, you can run your bot script using the `game-client` command:

```bash
game-client run your_bot_module:your_bot_instance
```

For example, if your bot is in a file named `my_bot.py` and the `GameClient` instance is named `app`, you would run:

```bash
game-client run my_bot:app
```

### Example Bot

Here is an example of a simple bot that explores the game world, attacks monsters, and heals when necessary.

```python
import enum

from programming_game.logging import logger
from programming_game.client import GameClient
from programming_game.schema.intents import AnyIntent
from programming_game.schema.position import Position
from programming_game.schema.units import Monster
from programming_game.structure.game_state import GameState
from programming_game.utils import get_distance


class State(enum.Enum):
    connected = 0
    explore = 1
    heal = 2


corner = 0
state: State = State.connected
tpos: Position | None = None


def set_state(new_state: State):
    global state
    logger.info(f"Setting state to {new_state}")
    state = new_state


app = GameClient(
    credentials={},
    enable_db=True,  # Enable database logging
)


@app.on_event("*")
async def on_event(payload: dict):
    pass


def _explore(gs: GameState):
    global corner, tpos

    if not tpos:
        corner = 0
        tpos = Position(-5, 5)

    if move := gs.player.move_to(tpos.x, tpos.y):
        return move

    if corner == 0:
        corner = 1
        tpos = Position(5, 5)
    else:
        corner = 0
        tpos = Position(-5, -5)

    return None


def explore(gs: GameState):
    player = gs.player
    units = gs.units

    monsters = [
        unit for unit in units.values() if type(unit) == Monster and unit.hp > 0
    ]

    if monsters:
        closest_monster = min(
            monsters, key=lambda m: get_distance(player.position, m.position)
        )
        return player.attack(closest_monster.id)

    if move := player.move_to(-25, -25):
        return move
    return None


def just_connected(gs: GameState) -> AnyIntent | None:
    if gs.player.hp > 70:
        set_state(State.explore)
    else:
        set_state(State.heal)
    return None

```

### Database Features

When database integration is enabled, the client automatically logs all events and intents to PostgreSQL with TimescaleDB for analytics.

#### Accessing Database Sessions

```python
# Get a database session for custom queries
async with app.get_db_session() as session:
    # Your database operations here
    result = await session.execute(text("SELECT COUNT(*) FROM events"))
    count = result.scalar()
    print(f"Total events: {count}")
```

#### Queuing Custom Events

```python
# Log custom events for analysis
await app.queue_event({
    "action": "custom_action",
    "data": {"key": "value"}
}, user_id="your_user_id")
```

#### Analytics with Grafana

The events table is optimized for time-series queries. Create dashboards showing:
- Events per second
- Bot activity patterns
- Combat statistics
- Resource usage over time

## API Documentation

This section provides an overview of the available actions, spells, and weapon skills in the game.

### Skills

*   **Attack**: Deal damage to an enemy.
    `return player.attack(enemy.id)`
*   **Move**: Move to a new location.
    `return player.move({ x: 1, y: 0 })`
*   **Respawn**: Discard all of your inventory and equipment, and respawn at a new location.
    `return player.respawn()`
*   **Summon Mana**: Focus your concentration to refill your available mana.
    `return player.summonMana()`
*   **Eat**: Eat food to restore your calories.
    `return player.eat(food.id)`
*   **Equip Spell**: Equip a spell stone from your inventory.
    `return player.equipSpell('fireball')`
*   **Unequip Spell**: Unequip the first spell in your spellbook.
    `return player.unequipSpell()`
*   **Cast**: Cast a spell.
    `return player.cast('fireball', enemy)`
*   **Sell**: Offer to sell items to another character.
    `return player.sell({ items: { snakeMeat: 10 }, to: npc })`
*   **Buy**: Buy items from another character.
    `return player.buy({ items: { snakeMeat: 10 }, from: npc })`
*   **Use**: Use an item.
    `return player.use('minorHealthPotion')`
*   **Equip**: Equip an item.
    `return player.equip('copperSword', 'weapon')`
*   **Unequip**: Unequip an item from your equipment.
    `return player.unequip('weapon')`
*   **Set Role**: Announce the role that you wish to player in your party.
    `return player.setRole(ROLES.healer)`
*   **Invite to Party**: Invite another unit to join your party.
    `return player.inviteToParty(target.id)`
*   **Seek Party**: Announce that you are looking for a party.
    `return player.seekParty()`
*   **Accept Party Invite**: Accept a party invite from another unit.
    `return player.acceptPartyInvite(inviter.id)`
*   **Decline Party Invite**: Decline a party invite from another unit.
    `return player.declinePartyInvite(inviter.id)`
*   **Leave Party**: Leave your current party.
    `return player.leaveParty()`
*   **Craft Item**: Craft an item using resources.
    `return player.craft('copperIngot', { chunkOfCopper: ingotCost })`
*   **Use Weapon Skill**: Use a weapon skill.
    `return player.useWeaponSkill({ skill: 'doubleSlash', target: enemy })`
*   **Drop Item**: Drop an item from your inventory.
    `return player.drop({ item: item.id, amount })`
*   **Set Trade**: Announce offers and requests for trades.
    `return player.setTrade({ buying: { snakeEyes: { quantity: 100, price: 10 } }, selling: { snakeMeat: { quantity: 100, price: 5 } } })`
*   **Accept Quest**: Accept a quest from an NPC.
    `return player.acceptQuest(npc, 'chicken_chaser')`
*   **Abandon Quest**: Abandon a quest that you've already accepted.
    `return player.abandonQuest('chicken_chaser')`
*   **Turn In Quest**: Turn in a quest that you've completed.
    `return player.turnInQuest(npc, 'chicken_chaser')`
*   **Deposit Items Into Storage**: Deposit items into your storage.
    `return player.deposit(banker, { copperCoin: 100 })`
*   **Withdraw Items From Storage**: Withdraw items from your storage.
    `return player.withdraw(banker, { copperCoin: 100 })`

### Spells

*   **Regen**: Instant, 3mp. Apply a buff that heals a small amount of health over time.
*   **Fireball**: 3s cast time, 7mp. Deal damage to an enemy.
*   **Heal**: 1.5s cast time, 5mp. Heal a large amount of health.
*   **Icicle**: 3s cast time, 5mp. Deal damage to an enemy.
*   **Ice Armor**: 2s cast time, 5mp. Grant a shield that absorbs damage.
*   **Chill**: Instant, 3mp. Slow an enemy.
*   **Ball of Ice**: 3s cast time, 5mp. Deal damage to an enemy.
*   **Flash Freeze**: Instant, 5mp. Deal damage to an enemy.
*   **Aid Digestion**: Channeled, 5mp/s. Heal a small amount of health over time.

### Weapon Skills

*   **Swords**:
    *   **Double Slash**: tp cost: 10. Slash twice, dealing damage to an enemy.
*   **Bows**:
    *   **Misdirecting Shot**: tp cost: 15. Shoot an arrow at an enemy, dealing damage and redirecting their threat.
    *   **Pinning Shot**: tp cost: 30. Shoot an arrow at an enemy, dealing damage and rooting them in place.
*   **Unarmed**:
    *   **Combo**: tp cost: 30. Attack three times, dealing damage to an enemy.
    *   **Haymaker**: tp cost: 30. A mighty punch, dealing damage to an enemy.
    *   **Headbutt**: tp cost: 40. Headbutt an enemy, dealing damage and stunning them.
*   **Greatswords**:
    *   **Charge**: tp cost: 20. Charge at an enemy, dealing damage and knocking them back.
    *   **Power Slash**: tp cost: 20. A powerful slash, dealing damage to an enemy.
*   **Shields**:
    *   **Shield Charge**: tp cost: 20. Charge at an enemy, dealing damage and stunning them.
*   **Usable with all weapons**:
    *   **Threaten**: tp cost: 10. Threaten an enemy, increasing their threat towards you.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "programming-game",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.11",
    "maintainer_email": null,
    "keywords": null,
    "author": null,
    "author_email": "Bastian Hoyer <dafire@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/a5/5d/66c83cf8f38c05514de8a9dc0dc31ffc3a5d8f0a0fa66433d4a490ac60c4/programming_game-0.0.17.tar.gz",
    "platform": null,
    "description": "# Programming Game Client\n\nA Python client for the Programming Game. This package allows you to write bots to play the game.\n\n## Installation\n\nTo install the package, run the following command:\n\n```bash\npip install programming-game\n```\n\n### Database Integration (Optional)\n\nFor database logging and analytics, install with database support:\n\n```bash\npip install programming-game[db]\n```\n\nSet up your PostgreSQL database with TimescaleDB:\n\n```sql\nCREATE DATABASE programming_game;\nCREATE EXTENSION IF NOT EXISTS timescaledb;\n```\n\nConfigure the database URL:\n\n```bash\nexport DATABASE_URL=\"postgresql://user:password@localhost/programming_game\"\n```\n\n## Usage\n\nTo run a bot, you need to provide your credentials via environment variables.\n\n```bash\nexport GAME_CLIENT_ID=\"YOUR_CLIENT_ID\"\nexport GAME_CLIENT_KEY=\"YOUR_CLIENT_KEY\"\n```\n\nThen, you can run your bot script using the `game-client` command:\n\n```bash\ngame-client run your_bot_module:your_bot_instance\n```\n\nFor example, if your bot is in a file named `my_bot.py` and the `GameClient` instance is named `app`, you would run:\n\n```bash\ngame-client run my_bot:app\n```\n\n### Example Bot\n\nHere is an example of a simple bot that explores the game world, attacks monsters, and heals when necessary.\n\n```python\nimport enum\n\nfrom programming_game.logging import logger\nfrom programming_game.client import GameClient\nfrom programming_game.schema.intents import AnyIntent\nfrom programming_game.schema.position import Position\nfrom programming_game.schema.units import Monster\nfrom programming_game.structure.game_state import GameState\nfrom programming_game.utils import get_distance\n\n\nclass State(enum.Enum):\n    connected = 0\n    explore = 1\n    heal = 2\n\n\ncorner = 0\nstate: State = State.connected\ntpos: Position | None = None\n\n\ndef set_state(new_state: State):\n    global state\n    logger.info(f\"Setting state to {new_state}\")\n    state = new_state\n\n\napp = GameClient(\n    credentials={},\n    enable_db=True,  # Enable database logging\n)\n\n\n@app.on_event(\"*\")\nasync def on_event(payload: dict):\n    pass\n\n\ndef _explore(gs: GameState):\n    global corner, tpos\n\n    if not tpos:\n        corner = 0\n        tpos = Position(-5, 5)\n\n    if move := gs.player.move_to(tpos.x, tpos.y):\n        return move\n\n    if corner == 0:\n        corner = 1\n        tpos = Position(5, 5)\n    else:\n        corner = 0\n        tpos = Position(-5, -5)\n\n    return None\n\n\ndef explore(gs: GameState):\n    player = gs.player\n    units = gs.units\n\n    monsters = [\n        unit for unit in units.values() if type(unit) == Monster and unit.hp > 0\n    ]\n\n    if monsters:\n        closest_monster = min(\n            monsters, key=lambda m: get_distance(player.position, m.position)\n        )\n        return player.attack(closest_monster.id)\n\n    if move := player.move_to(-25, -25):\n        return move\n    return None\n\n\ndef just_connected(gs: GameState) -> AnyIntent | None:\n    if gs.player.hp > 70:\n        set_state(State.explore)\n    else:\n        set_state(State.heal)\n    return None\n\n```\n\n### Database Features\n\nWhen database integration is enabled, the client automatically logs all events and intents to PostgreSQL with TimescaleDB for analytics.\n\n#### Accessing Database Sessions\n\n```python\n# Get a database session for custom queries\nasync with app.get_db_session() as session:\n    # Your database operations here\n    result = await session.execute(text(\"SELECT COUNT(*) FROM events\"))\n    count = result.scalar()\n    print(f\"Total events: {count}\")\n```\n\n#### Queuing Custom Events\n\n```python\n# Log custom events for analysis\nawait app.queue_event({\n    \"action\": \"custom_action\",\n    \"data\": {\"key\": \"value\"}\n}, user_id=\"your_user_id\")\n```\n\n#### Analytics with Grafana\n\nThe events table is optimized for time-series queries. Create dashboards showing:\n- Events per second\n- Bot activity patterns\n- Combat statistics\n- Resource usage over time\n\n## API Documentation\n\nThis section provides an overview of the available actions, spells, and weapon skills in the game.\n\n### Skills\n\n*   **Attack**: Deal damage to an enemy.\n    `return player.attack(enemy.id)`\n*   **Move**: Move to a new location.\n    `return player.move({ x: 1, y: 0 })`\n*   **Respawn**: Discard all of your inventory and equipment, and respawn at a new location.\n    `return player.respawn()`\n*   **Summon Mana**: Focus your concentration to refill your available mana.\n    `return player.summonMana()`\n*   **Eat**: Eat food to restore your calories.\n    `return player.eat(food.id)`\n*   **Equip Spell**: Equip a spell stone from your inventory.\n    `return player.equipSpell('fireball')`\n*   **Unequip Spell**: Unequip the first spell in your spellbook.\n    `return player.unequipSpell()`\n*   **Cast**: Cast a spell.\n    `return player.cast('fireball', enemy)`\n*   **Sell**: Offer to sell items to another character.\n    `return player.sell({ items: { snakeMeat: 10 }, to: npc })`\n*   **Buy**: Buy items from another character.\n    `return player.buy({ items: { snakeMeat: 10 }, from: npc })`\n*   **Use**: Use an item.\n    `return player.use('minorHealthPotion')`\n*   **Equip**: Equip an item.\n    `return player.equip('copperSword', 'weapon')`\n*   **Unequip**: Unequip an item from your equipment.\n    `return player.unequip('weapon')`\n*   **Set Role**: Announce the role that you wish to player in your party.\n    `return player.setRole(ROLES.healer)`\n*   **Invite to Party**: Invite another unit to join your party.\n    `return player.inviteToParty(target.id)`\n*   **Seek Party**: Announce that you are looking for a party.\n    `return player.seekParty()`\n*   **Accept Party Invite**: Accept a party invite from another unit.\n    `return player.acceptPartyInvite(inviter.id)`\n*   **Decline Party Invite**: Decline a party invite from another unit.\n    `return player.declinePartyInvite(inviter.id)`\n*   **Leave Party**: Leave your current party.\n    `return player.leaveParty()`\n*   **Craft Item**: Craft an item using resources.\n    `return player.craft('copperIngot', { chunkOfCopper: ingotCost })`\n*   **Use Weapon Skill**: Use a weapon skill.\n    `return player.useWeaponSkill({ skill: 'doubleSlash', target: enemy })`\n*   **Drop Item**: Drop an item from your inventory.\n    `return player.drop({ item: item.id, amount })`\n*   **Set Trade**: Announce offers and requests for trades.\n    `return player.setTrade({ buying: { snakeEyes: { quantity: 100, price: 10 } }, selling: { snakeMeat: { quantity: 100, price: 5 } } })`\n*   **Accept Quest**: Accept a quest from an NPC.\n    `return player.acceptQuest(npc, 'chicken_chaser')`\n*   **Abandon Quest**: Abandon a quest that you've already accepted.\n    `return player.abandonQuest('chicken_chaser')`\n*   **Turn In Quest**: Turn in a quest that you've completed.\n    `return player.turnInQuest(npc, 'chicken_chaser')`\n*   **Deposit Items Into Storage**: Deposit items into your storage.\n    `return player.deposit(banker, { copperCoin: 100 })`\n*   **Withdraw Items From Storage**: Withdraw items from your storage.\n    `return player.withdraw(banker, { copperCoin: 100 })`\n\n### Spells\n\n*   **Regen**: Instant, 3mp. Apply a buff that heals a small amount of health over time.\n*   **Fireball**: 3s cast time, 7mp. Deal damage to an enemy.\n*   **Heal**: 1.5s cast time, 5mp. Heal a large amount of health.\n*   **Icicle**: 3s cast time, 5mp. Deal damage to an enemy.\n*   **Ice Armor**: 2s cast time, 5mp. Grant a shield that absorbs damage.\n*   **Chill**: Instant, 3mp. Slow an enemy.\n*   **Ball of Ice**: 3s cast time, 5mp. Deal damage to an enemy.\n*   **Flash Freeze**: Instant, 5mp. Deal damage to an enemy.\n*   **Aid Digestion**: Channeled, 5mp/s. Heal a small amount of health over time.\n\n### Weapon Skills\n\n*   **Swords**:\n    *   **Double Slash**: tp cost: 10. Slash twice, dealing damage to an enemy.\n*   **Bows**:\n    *   **Misdirecting Shot**: tp cost: 15. Shoot an arrow at an enemy, dealing damage and redirecting their threat.\n    *   **Pinning Shot**: tp cost: 30. Shoot an arrow at an enemy, dealing damage and rooting them in place.\n*   **Unarmed**:\n    *   **Combo**: tp cost: 30. Attack three times, dealing damage to an enemy.\n    *   **Haymaker**: tp cost: 30. A mighty punch, dealing damage to an enemy.\n    *   **Headbutt**: tp cost: 40. Headbutt an enemy, dealing damage and stunning them.\n*   **Greatswords**:\n    *   **Charge**: tp cost: 20. Charge at an enemy, dealing damage and knocking them back.\n    *   **Power Slash**: tp cost: 20. A powerful slash, dealing damage to an enemy.\n*   **Shields**:\n    *   **Shield Charge**: tp cost: 20. Charge at an enemy, dealing damage and stunning them.\n*   **Usable with all weapons**:\n    *   **Threaten**: tp cost: 10. Threaten an enemy, increasing their threat towards you.\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "A Python client for the Programming Game.",
    "version": "0.0.17",
    "project_urls": {
        "Homepage": "https://github.com/bastianh/programming-game-python",
        "Repository": "https://github.com/bastianh/programming-game-python"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "290151fce025e96754b23bfe9e890f386f3f79f3386a58221598a11f9c23da36",
                "md5": "0270c0935bf7dbbe63d47e593657dd2e",
                "sha256": "f891dcc5e4031ed542cd88fbff68abe2cfe37561c0cd6de26318acde8c97bc77"
            },
            "downloads": -1,
            "filename": "programming_game-0.0.17-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "0270c0935bf7dbbe63d47e593657dd2e",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.11",
            "size": 41280,
            "upload_time": "2025-09-16T16:35:41",
            "upload_time_iso_8601": "2025-09-16T16:35:41.999369Z",
            "url": "https://files.pythonhosted.org/packages/29/01/51fce025e96754b23bfe9e890f386f3f79f3386a58221598a11f9c23da36/programming_game-0.0.17-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "a55d66c83cf8f38c05514de8a9dc0dc31ffc3a5d8f0a0fa66433d4a490ac60c4",
                "md5": "0ad3f81c074c479311ca4ae2750899dd",
                "sha256": "0606bb7f68e93d3907e9ba118572bf18d1c56ca850d619bdda0ddc733814361b"
            },
            "downloads": -1,
            "filename": "programming_game-0.0.17.tar.gz",
            "has_sig": false,
            "md5_digest": "0ad3f81c074c479311ca4ae2750899dd",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.11",
            "size": 35378,
            "upload_time": "2025-09-16T16:35:43",
            "upload_time_iso_8601": "2025-09-16T16:35:43.814101Z",
            "url": "https://files.pythonhosted.org/packages/a5/5d/66c83cf8f38c05514de8a9dc0dc31ffc3a5d8f0a0fa66433d4a490ac60c4/programming_game-0.0.17.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-09-16 16:35:43",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "bastianh",
    "github_project": "programming-game-python",
    "github_not_found": true,
    "lcname": "programming-game"
}
        
Elapsed time: 1.42355s