# Pymerc
> A Python library for interacting with the [Mercatorio] browser based game
## Usage
You must first [generate API credentials](https://play.mercatorio.io/settings/api).
Once generated, you can instantiate a `Client` instance using the credentials.
```python
from pymerc.client import Client
# Create a new client
client = Client(os.environ["API_USER"], os.environ["API_TOKEN"])
```
### Game Objects
The `pymerc` package provides both low-level API calls as well as high-level objects that wrap those calls.
In almost all cases, you should plan to use the higher-level objects.
If something is missing in one of these objects, please submit an issue or PR.
Most logic is contained within the `Player` object:
```python
player = await client.player()
```
Creating the player object can take a few seconds as it results in several requests being sent to the API.
It's recommended you re-use the player object instead of recreating it multiple times.
The data for the player object can be updated with:
```python
await player.load()
```
#### Storehouse
Check our balance of beer and then buy some from the local market:
```python
>>> from pymerc.api.models.common import Item
>>> beer = player.storehouse.items[Item.Beer]
>>> beer.balance
41.5
>>> beer.market_data
TownMarketItem(price=2.894, last_price=2.894, average_price=2.894, moving_average=2.868, highest_bid=2.894, lowest_ask=3.0, volume=95, volume_prev_12=1085, bid_volume_10=2, ask_volume_10=20)
>>> details = await beer.fetch_market_details() # More fine-grained details
>>> details.asks
[ItemOrder(volume=20, price=3.0),
ItemOrder(volume=1, price=3.425),
ItemOrder(volume=3, price=3.475),
ItemOrder(volume=1, price=3.526)]
>>> result = await beer.buy(1, 3.0)
>>> result.settlements[0].volume
1
```
Adjust the price and volume of beer we are selling:
```python
>>> beer.manager
InventoryManager(buy_price=5.45, buy_volume=0, capacity=100, max_holding=None, sell_price=2.8, sell_volume=25)
>>> await beer.patch_manager(sell_price=2.7, sell_volume=26)
>>> player.storehouse.items[Item.Beer].manager
InventoryManager(buy_price=5.45, buy_volume=0, capacity=100, max_holding=None, sell_price=2.7, sell_volume=26)
```
#### Transports
Load the transport that is currently docked in Aderhampton:
```python
>>> tr = player.transports.by_town_name('Aderhampton')[0]
>>> tr.docked
True
```
List the items we are exporting here:
```python
>>> list(tr.exports.keys())
[<Item.Cloth: 'cloth'>,
<Item.DyedCloth: 'dyed cloth'>,
<Item.Garments: 'garments'>]
```
Check how much cloth we exported last turn:
```python
>>> tr.exports[Item.Cloth].manager.sell_volume
37
>>> tr.exports[Item.Cloth].volume_flowed
37
```
Looks like we are at max capacity for our export.
Bump our export volume and then buy some more cloth off the Aderhampton market:
```python
>>> await tr.exports[Item.Cloth].patch_manager(sell_volume=38)
>>> details = await tr.exports[Item.Cloth].fetch_market_details()
>>> details.bids
[ItemOrder(volume=1, price=8.99),
ItemOrder(volume=3, price=8.856),
ItemOrder(volume=1, price=8.822),
ItemOrder(volume=1, price=8.478),
ItemOrder(volume=5, price=8.441),
ItemOrder(volume=1, price=5.414)]
>>> player.storehouse.items[Item.Cloth].balance
1477.096
>>> await tr.exports[Item.Cloth].sell(1, 8.99)
>>> player.storehouse.items[Item.Cloth].balance
1476.096
```
#### Operations
Get the operations for all of our weaveries:
```python
>>> from pymerc.api.models.common import BuildingType
>>> player.operations.by_building_type(BuildingType.Weavery)
OperationsList([<pymerc.game.operation.Operation at 0x7ffbb00de6f0>])
```
We have a single operation going on associated with a weavery.
Check how much it is currently outputting:
```python
>>> player.operations.by_building_type(BuildingType.Weavery).outputs
{<Item.Cloth: 'cloth'>: 400.0}
```
Check all of our operations that are taking cloth as an input:
```python
>>> player.operations.by_item_input(Item.Cloth)
OperationsList([<pymerc.game.operation.Operation at 0x7ffbb00debd0>,
<pymerc.game.operation.Operation at 0x7ffbb00dede0>,
<pymerc.game.operation.Operation at 0x7ffbb00dee70>,
<pymerc.game.operation.Operation at 0x7ffbb00dcf80>])
```
Check how much cloth all of these operations are consuming in total:
```python
>>> player.operations.by_item_input(Item.Cloth).inputs[Item.Cloth]
197.0
```
#### Data Analysis
Compare our total and actual imports:
```python
>>> player.imports.volume
426
>>> player.imports.volume_flowed
281 # Importing a little over half of our target volume
>>> player.imports.cost
3669.11
>>> player.imports.cost_flowed
1715.167 # Importing at half of our target cost (yay!)
```
See how our cloth production is doing:
```python
>>> cloth = player.storehouse.items[Item.Cloth]
>>> cloth.produced
400.0
>>> cloth.production_cost
2383.614
>>> excess = cloth.produced - cloth.consumed
>>> excess_value = excess * cloth.average_cost
>>> value_flowed = cloth.sale_value + player.exports[Item.Cloth].value_flowed
>>> value_flowed > excess_value
False # Looks like we're not making money on our excess cloth!
```
## Testing
Since this library parses live API endpoints, mocking values makes little sense.
Instead, you must provide a `.env` file with your API credentials:
```text
API_USER="<USER>"
API_TOKEN="<TOKEN>"
```
The tests will utilize this to validate that all endpoints are parsing correctly:
```shell
pytest .
```
Additionally, you can create an instance of the client/player to test with using `ipython`:
```shell
> ipython
In [1]: from shell import main; await main(); from shell import client;
In [2]: player = await client.player()
```
[Mercatorio]: https://mercatorio.io
Raw data
{
"_id": null,
"home_page": "https://github.com/jmgilman/pymerc",
"name": "pymerc",
"maintainer": null,
"docs_url": null,
"requires_python": "<4.0,>=3.11",
"maintainer_email": null,
"keywords": "mercatorio, python, library, game",
"author": "Joshua Gilman",
"author_email": "joshuagilman@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/07/54/c7ce42801127c06ea3dc1d992c1203f20e8c8398eaa422644b9899aa06b8/pymerc-0.6.0.tar.gz",
"platform": null,
"description": "# Pymerc\n\n> A Python library for interacting with the [Mercatorio] browser based game\n\n## Usage\n\nYou must first [generate API credentials](https://play.mercatorio.io/settings/api).\nOnce generated, you can instantiate a `Client` instance using the credentials.\n\n```python\nfrom pymerc.client import Client\n\n# Create a new client\nclient = Client(os.environ[\"API_USER\"], os.environ[\"API_TOKEN\"])\n```\n\n### Game Objects\n\nThe `pymerc` package provides both low-level API calls as well as high-level objects that wrap those calls.\nIn almost all cases, you should plan to use the higher-level objects.\nIf something is missing in one of these objects, please submit an issue or PR.\n\nMost logic is contained within the `Player` object:\n\n```python\nplayer = await client.player()\n```\n\nCreating the player object can take a few seconds as it results in several requests being sent to the API.\nIt's recommended you re-use the player object instead of recreating it multiple times.\nThe data for the player object can be updated with:\n\n```python\nawait player.load()\n```\n\n#### Storehouse\n\nCheck our balance of beer and then buy some from the local market:\n\n```python\n>>> from pymerc.api.models.common import Item\n>>> beer = player.storehouse.items[Item.Beer]\n>>> beer.balance\n41.5\n>>> beer.market_data\nTownMarketItem(price=2.894, last_price=2.894, average_price=2.894, moving_average=2.868, highest_bid=2.894, lowest_ask=3.0, volume=95, volume_prev_12=1085, bid_volume_10=2, ask_volume_10=20)\n>>> details = await beer.fetch_market_details() # More fine-grained details\n>>> details.asks\n[ItemOrder(volume=20, price=3.0),\n ItemOrder(volume=1, price=3.425),\n ItemOrder(volume=3, price=3.475),\n ItemOrder(volume=1, price=3.526)]\n>>> result = await beer.buy(1, 3.0)\n>>> result.settlements[0].volume\n1\n```\n\nAdjust the price and volume of beer we are selling:\n\n```python\n>>> beer.manager\nInventoryManager(buy_price=5.45, buy_volume=0, capacity=100, max_holding=None, sell_price=2.8, sell_volume=25)\n>>> await beer.patch_manager(sell_price=2.7, sell_volume=26)\n>>> player.storehouse.items[Item.Beer].manager\nInventoryManager(buy_price=5.45, buy_volume=0, capacity=100, max_holding=None, sell_price=2.7, sell_volume=26)\n```\n\n#### Transports\n\nLoad the transport that is currently docked in Aderhampton:\n\n```python\n>>> tr = player.transports.by_town_name('Aderhampton')[0]\n>>> tr.docked\nTrue\n```\n\nList the items we are exporting here:\n\n```python\n>>> list(tr.exports.keys())\n[<Item.Cloth: 'cloth'>,\n <Item.DyedCloth: 'dyed cloth'>,\n <Item.Garments: 'garments'>]\n```\n\nCheck how much cloth we exported last turn:\n\n```python\n>>> tr.exports[Item.Cloth].manager.sell_volume\n37\n>>> tr.exports[Item.Cloth].volume_flowed\n37\n```\n\nLooks like we are at max capacity for our export.\nBump our export volume and then buy some more cloth off the Aderhampton market:\n\n```python\n>>> await tr.exports[Item.Cloth].patch_manager(sell_volume=38)\n>>> details = await tr.exports[Item.Cloth].fetch_market_details()\n>>> details.bids\n[ItemOrder(volume=1, price=8.99),\n ItemOrder(volume=3, price=8.856),\n ItemOrder(volume=1, price=8.822),\n ItemOrder(volume=1, price=8.478),\n ItemOrder(volume=5, price=8.441),\n ItemOrder(volume=1, price=5.414)]\n>>> player.storehouse.items[Item.Cloth].balance\n1477.096\n>>> await tr.exports[Item.Cloth].sell(1, 8.99)\n>>> player.storehouse.items[Item.Cloth].balance\n1476.096\n```\n\n#### Operations\n\nGet the operations for all of our weaveries:\n\n```python\n>>> from pymerc.api.models.common import BuildingType\n>>> player.operations.by_building_type(BuildingType.Weavery)\nOperationsList([<pymerc.game.operation.Operation at 0x7ffbb00de6f0>])\n```\n\nWe have a single operation going on associated with a weavery.\nCheck how much it is currently outputting:\n\n```python\n>>> player.operations.by_building_type(BuildingType.Weavery).outputs\n{<Item.Cloth: 'cloth'>: 400.0}\n```\n\nCheck all of our operations that are taking cloth as an input:\n\n```python\n>>> player.operations.by_item_input(Item.Cloth)\nOperationsList([<pymerc.game.operation.Operation at 0x7ffbb00debd0>,\n <pymerc.game.operation.Operation at 0x7ffbb00dede0>,\n <pymerc.game.operation.Operation at 0x7ffbb00dee70>,\n <pymerc.game.operation.Operation at 0x7ffbb00dcf80>])\n```\n\nCheck how much cloth all of these operations are consuming in total:\n\n```python\n>>> player.operations.by_item_input(Item.Cloth).inputs[Item.Cloth]\n197.0\n```\n\n#### Data Analysis\n\nCompare our total and actual imports:\n\n```python\n>>> player.imports.volume\n426\n>>> player.imports.volume_flowed\n281 # Importing a little over half of our target volume\n>>> player.imports.cost\n3669.11\n>>> player.imports.cost_flowed\n1715.167 # Importing at half of our target cost (yay!)\n```\n\nSee how our cloth production is doing:\n\n```python\n>>> cloth = player.storehouse.items[Item.Cloth]\n>>> cloth.produced\n400.0\n>>> cloth.production_cost\n2383.614\n>>> excess = cloth.produced - cloth.consumed\n>>> excess_value = excess * cloth.average_cost\n>>> value_flowed = cloth.sale_value + player.exports[Item.Cloth].value_flowed\n>>> value_flowed > excess_value\nFalse # Looks like we're not making money on our excess cloth!\n```\n\n## Testing\n\nSince this library parses live API endpoints, mocking values makes little sense.\nInstead, you must provide a `.env` file with your API credentials:\n\n```text\nAPI_USER=\"<USER>\"\nAPI_TOKEN=\"<TOKEN>\"\n```\n\nThe tests will utilize this to validate that all endpoints are parsing correctly:\n\n```shell\npytest .\n```\n\nAdditionally, you can create an instance of the client/player to test with using `ipython`:\n\n```shell\n> ipython\nIn [1]: from shell import main; await main(); from shell import client;\nIn [2]: player = await client.player()\n```\n\n[Mercatorio]: https://mercatorio.io\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A Python library for interacting with the Mercatorio browser based game",
"version": "0.6.0",
"project_urls": {
"Homepage": "https://github.com/jmgilman/pymerc"
},
"split_keywords": [
"mercatorio",
" python",
" library",
" game"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "0cb481c770d64253c93186e6711bf15c4cd822c6bddf538def3ff2829d7c1c9d",
"md5": "f9379403957140baec6ce7bdfcc0f84e",
"sha256": "16727c81bce07beff4a8a07ab7d0eacb99a51523e228b6d1b3c6632b15218d69"
},
"downloads": -1,
"filename": "pymerc-0.6.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "f9379403957140baec6ce7bdfcc0f84e",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4.0,>=3.11",
"size": 36974,
"upload_time": "2024-06-02T19:21:47",
"upload_time_iso_8601": "2024-06-02T19:21:47.003836Z",
"url": "https://files.pythonhosted.org/packages/0c/b4/81c770d64253c93186e6711bf15c4cd822c6bddf538def3ff2829d7c1c9d/pymerc-0.6.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "0754c7ce42801127c06ea3dc1d992c1203f20e8c8398eaa422644b9899aa06b8",
"md5": "1591aa5dbdd89033699c5a32b2359b30",
"sha256": "5a3c03764d47b4d67d7a374ba47f0b0e52a3dd537d1010d659da0675e1cd3d82"
},
"downloads": -1,
"filename": "pymerc-0.6.0.tar.gz",
"has_sig": false,
"md5_digest": "1591aa5dbdd89033699c5a32b2359b30",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4.0,>=3.11",
"size": 27252,
"upload_time": "2024-06-02T19:21:48",
"upload_time_iso_8601": "2024-06-02T19:21:48.722229Z",
"url": "https://files.pythonhosted.org/packages/07/54/c7ce42801127c06ea3dc1d992c1203f20e8c8398eaa422644b9899aa06b8/pymerc-0.6.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-06-02 19:21:48",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "jmgilman",
"github_project": "pymerc",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "pymerc"
}