Chapter 2 - Understanding the unknown

Hello human. Good to see you again.

I understand that having you reading this is an expression of the desire of knowing more about the universe around you, and eventually leave your planet.

The next step is to know more about the galaxy. How many planets are? How far are they from you? Do you have starships? How can you use them?

Keep reading, and all your questions will be answered.

The galaxy, an introduction

In Pythonium, the galaxy is the source of all truth for you. It represents all your owned knowledge about the universe, and in most cases, all the information to develop your strategy will be extracted from the galaxy.

First, you need to learn what do you know about the galaxy. To do so we will use ipdb, the ancient oracle of python code.

This tool allows you to see what’s going on with your python code at some point. In our case, we want to know what’s going on at the beginning of each turn.

Note

Don’t you know ipdb? Check it out.

Open the player you built in Chapter 1 and set a trace in your next_turn method:

from pyhtonium import AbstractPlayer

class Player(AbstractPlayer):

    name = 'Han Solo'

    def next_turn(self, galaxy, context):
        import ipdb; ipdb.set_trace()
        return galaxy

Once executed you will see something similar to:

         8     def next_turn(self, galaxy, context):
         9         import ipdb; ipdb.set_trace()
---> 10         return galaxy

ipdb> _

Note

If you don’t remember how to do execute your player check on Executing your player

Now we can start investigating the galaxy.

ipdb> galaxy
Galaxy(size=(500, 500), planets=300)

Ok then, this means you are in a galaxy of 500 light-years width and 500 ly height (size=(500, 500)) compounded by 300 planets (planets=300).

There are three main galaxy attributes that you must know in deep.

turn

Your time reference. The turn that is being played.

ipdb> galaxy.turn
0

As expected, the game just began, and you are in turn 0.

To move one turn forward, use the c command.

ipdb> c

         8     def next_turn(self, galaxy, context):
         9         import ipdb; ipdb.set_trace()
---> 10         return galaxy

ipdb> galaxy.turn
1

Note

And as you may suspect, there is no way to come back in time. Time always moves forward.

planets

This attribute stores the state of all the planets in the galaxy.

galaxy.planets is a python dictionary where the keys are planet’s Position, and the values are Planet instances.

ipdb> type(galaxy.planets)
<class 'dict'>

ipdb> pp galaxy.planets
{(2, 124): Planet(id=ecf5f0b9-d639-48fb-ac06-cb0027d03d5b, position=(2, 124), player=None),
 (3, 466): Planet(id=b20406cb-b764-4842-8dac-ec13c2038ca9, position=(3, 466), player=None),
 (4, 129): Planet(id=ec53e2a9-24e2-49f5-aa56-4a6337b06b87, position=(4, 129), player=None),
 (4, 294): Planet(id=40712b86-5bf3-453f-9714-760dbe771570, position=(4, 294), player=None),
...
}

ipdb> len(galaxy.planets)
300

Note

In the previous example, we use the ipdb command pp, as an alias for pprint.

A planet has tons of attributes, for now we will focus just in a few of them:

  • id a unique identifier for the planet,

  • position is the planet position in the galaxy in (x, y) coordinates,

  • player is the planet’s owner, it can be None if the planet is not colonized or the owner is unknown to you.

ships

In a similar way as with the planets, the galaxy.ships attribute is a python list that stores references to every Ship in the galaxy.

ipdb> type(galaxy.ships)
<class 'list'>

ipdb> pp galaxy.ships
[Ship(id=b615699e-c70e-4e55-b678-fb0513abbb0b, position=(27, 23), player=Han Solo),
 Ship(id=5b8e15a8-a319-43d0-bdd0-6be675d1742e, position=(27, 23), player=Han Solo)]

ipdb> len(galaxy.ships)
2

The ships, also have id, position, and player attributes.

From galaxy.ships output we can tell there are two known ships in the galaxy, and both are yours (notice the player=Han Solo).

Querying to the galaxy

The galaxy has methods that allow you to filter ships and planets based on several criteria. In this section, we will present some receipts to answer common questions that you may have.

Where are my planets?

By looking carefully into the galaxy.planets output you will find a planet with player=Han Solo.

That’s your planet!

But you may be thinking there should be an easier way to find which planets are yours (if any). And there is: this can be done with the Galaxy.get_player_planets method.

This method takes a player name as attribute and returns an iterable with all the planets where the owner is the player with the name you asked for.

ipdb> my_planets = galaxy.get_player_planets(self.name)
ipdb> pp list(my_planets)
[Planet(id=1fa89759-6834-478a-9eda-6985dd95a0c7, position=(27, 23), player=Han Solo)]

Note

You can access to the name of your Player inside your next_turn method with the self.name attribute.

Where are my ships?

In a similar fashion to planets, you can find all your ships with the Galaxy.get_player_ships method.

ipdb> my_ships = galaxy.get_player_ships(self.name)
ipdb> pp list(my_ships)
[Ship(id=b615699e-c70e-4e55-b678-fb0513abbb0b, position=(27, 23), player=Han Solo),
 Ship(id=5b8e15a8-a319-43d0-bdd0-6be675d1742e, position=(27, 23), player=Han Solo)]

In single-player mode Galaxy.get_player_ships always returns all the ships in galaxy.ships, as there are no abandoned ships in pythonium (with player=None).

But in multiplayer mode, you can also find enemy ships in the galaxy.ships attribute. In that case, this function can be handy to get only your ships, or the visible enemy ships.

Are there ships on my planet orbit?

Let’s suppose you want to transfer some resource from one planet to another, the first thing you want to know is if there is any ship in the same position as your planet, to use this ship to transfer the resource.

This can be answered with the Galaxy.get_ships_in_position method.

This method takes a position as parameter and returns an iterable with all the known ships in that position.

In our case, that will be the position attribute of your planet.

ipdb> my_planets = galaxy.get_player_planets(self.name)
ipdb> some_planet = next(my_planets)
ipdb> some_planet
Planet(id=1fa89759-6834-478a-9eda-6985dd95a0c7, position=(27, 23), player=Han Solo)

ipdb> ships_in_planet = galaxy.get_ships_in_position(some_planet.position)
ipdb> pp list(ships_in_planet)
[Ship(id=b615699e-c70e-4e55-b678-fb0513abbb0b, position=(27, 23), player=Han Solo),
 Ship(id=5b8e15a8-a319-43d0-bdd0-6be675d1742e, position=(27, 23), player=Han Solo)]

Is my ship in a planet?

Now think the opposite example, you have a ship and you want to know if it is located on a planet or in deep space.

This can be answered by simply searching if there is planets in the ship’s position.

ipdb> my_ships = galaxy.get_player_ships(self.name)
ipdb> some_ship = next(my_ships)
ipdb> some_ship
Ship(id=b615699e-c70e-4e55-b678-fb0513abbb0b, position=(27, 23), player=Han Solo)

ipdb> planet = galaxy.planets.get(some_ship.position)
ipdb> planet
Planet(id=1fa89759-6834-478a-9eda-6985dd95a0c7, position=(27, 23), player=Han Solo)

Turn context

Apart from galaxy there is a second argument received by the Player.next_turn method: the turn context.

The context contains additional metadata about the turn and the overall game.

ipdb> type(context)
<class 'dict'>

ipdb> context.keys()
dict_keys(['ship_types', 'tolerable_taxes', 'happypoints_tolerance', 'score'])

Here we see that context is a dictionary with several keys. For now, we will focus on the score.

ipdb> context['score']
[{'turn': 1, 'player': 'Han Solo', 'planets': 1, 'ships_carrier': 2, 'ships_war': 0, 'total_ships': 2}]

From the score we know:

  • The current turn number is 1,

  • there is only one player called ‘Han Solo’ (that’s you!),

  • Han Solo owns,

    • one planet,

    • two carrier ships

    • zero warships,

    • and two ships in total

This is, in fact, consistent with the found results in previous sections. When you query your owned planets, the result was one single planet, and for your ships, the result was two ships.

You can verify that both ships are carriers by doing

ipdb> for ship in my_ships:
    print(ship.type.name)

carrier
carrier

In the next chapters, we will explore a bit more about the context, different ship types, and their attributes.

How to exit from the ipdb debugger

Pythonium has a special command for exit the ipdb. You will notice that the usual exit command will not work in this case. Exiting from the infinite loop of time is a bit more complex.

If you want to exit the debugger do:

ipdb> from pythonium.debugger import terminate
ipdb> terminate()

Final thoughts

In this chapter, we explained how to access the different objects from the galaxy, with a focus on those objects owned by your player. Depending on the complexity of the player that you want to implement, you might find useful one method or another. That is something you need to discover yourself, but it is good to have an overview.

You can also implement your own query methods for galaxy.planets and galaxy.ships depending on your needs. For starters space explorers, the methods presented in this section should be enough for most cases.

In the next chapter, you will learn how to move your ships.

Keep moving human, the battle for pythonium is waiting for you.