Name | programming-game JSON |
Version |
0.0.17
JSON |
| download |
home_page | None |
Summary | A Python client for the Programming Game. |
upload_time | 2025-09-16 16:35:43 |
maintainer | None |
docs_url | None |
author | None |
requires_python | >=3.11 |
license | MIT |
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"
}