Book Image

Learning Python Application Development

By : Ninad Sathaye
Book Image

Learning Python Application Development

By: Ninad Sathaye

Overview of this book

Python is one of the most widely used dynamic programming languages, supported by a rich set of libraries and frameworks that enable rapid development. But fast paced development often comes with its own baggage that could bring down the quality, performance, and extensibility of an application. This book will show you ways to handle such problems and write better Python applications. From the basics of simple command-line applications, develop your skills all the way to designing efficient and advanced Python apps. Guided by a light-hearted fantasy learning theme, overcome the real-world problems of complex Python development with practical solutions. Beginning with a focus on robustness, packaging, and releasing application code, you’ll move on to focus on improving application lifetime by making code extensible, reusable, and readable. Get to grips with Python refactoring, design patterns and best practices. Techniques to identify the bottlenecks and improve performance are covered in a series of chapters devoted to performance, before closing with a look at developing Python GUIs.
Table of Contents (18 chapters)
Learning Python Application Development
Credits
Disclaimers
About the Author
About the Reviewer
www.PacktPub.com
Preface
Index

Using functions – Attack of the Orcs v0.0.5


In the last section, you wrote a quick set of instructions to create a nice little command-line game. You asked your friends to try it out and they kind of liked it (perhaps they were just trying to be nice!). You received the first feature request for the game.

"I think this game has good potential to grow. How about including combat in the next version of the game? When Sir Foo encounters an enemy, he should not just give up that easily. Fight with the enemy! Let the combat decide the winner. "-your friend

You liked the idea and decided to add this capability to the code in the next version. Additionally, you also want to make it more interactive.

The script you wrote for the first program was small. However, as we go on adding new features, it will soon become a maintenance headache. As a step further, we will wrap the existing code into small functions so that the code is easier to manage. In functional programming, the focus is typically on function arrangement and their composition. For example, you can build complicated logic using a simple set of reusable functions.

Revisiting the previous version

Before adding any new features, let's revisit the script that you wrote in the previous version (version 0.0.1). We will identify the blocks of code that can be wrapped into functions. Such code chunks are marked in the two code snippets that follow:

We will wrap most of the highlighted code into individual functions, as follows:

1:  show_theme_message 
2:  show_game_mission 
3:  occupy_huts 
4:  process_user_choice 
5:  reveal_occupants
6:  enter_hut

In addition to these six blocks of code, we can also create a few top-level functions to handle all this logic. In Python, the function is created using the def keyword, followed by the function name and arguments in parentheses. For example, the reveal_occupants function requires the information about the huts list. We also need to optionally pass the dotted_line string if we do not want to recreate it in the function. So, we will pass the hut number idx, the huts list, and the dotted_line string as function arguments. This function can be written as follows:

After this initial work, the original script can be rewritten as:

This is much easier to read now. What we just did is also referred to as refactoring; more on various refactoring techniques in a later chapter. It makes it easier to do changes to the individual methods. For example, if you want to customize the mission statement or scenario description, you do not need to open the main function, run_application. Similarly, occupy_huts can be expanded further without any clutter in the main code.

Tip

The initial refactored version of the code is not perfect. There is plenty of room for improvement. Can you reduce the burden of passing the dotted_line parameter or think of some other way to handle the printing of bold text?

Pseudo code with attack feature – Version 0.0.5

In the previous section, we wrapped the game logic into individual functions. This not only improved the code readability, but also made it easier to maintain. Let's move on and include the new attack() function in the game. The following steps show the logic of the game with the attack feature included.

While the user wishes to keep playing the game:

  • Print game mission

  • Create a huts list

  • Randomly place 'enemy', 'friend', or 'unoccupied' in 5 huts

  • Prompt the player to select a hut number

  • if the hut has an enemy, do the following:

    • while the user wishes to continue the attack, use the attack() method on the enemy

      After each attack, update and show the health of Sir Foo, and of the enemy too; if enemy health <= 0: print "You Win".

      But, if Sir Foo health <= 0: print "You Lose".

  • else (hut has a friend or is unoccupied) print "you win"

Initially, Sir Foo and the Orc will have full health. To quantify health, let's assign hit points to each of these characters (or the game units). So, when we say the character has full health, it means it has the maximum possible hit points. Depending on the character, the default number of hit points will vary. The following image shows Sir Foo and the Orc with the default number of hit points, indicated by the Health label:

The bar above the Health label in the image represents a health meter. Essentially, it keeps track of the hit points. In the discussion that follows, we will use the terms hit points and health meter interchangeably. During the combat, either the player or the enemy will get injured. For now, neglect the third possibility where both escape unhurt. An injury will reduce the number of available hit points for the injured unit. In the game, we will assume that in a single attack turn only one of the characters is hit. The following image will help you imagine one such attack turn:

Here, Sir Foo's health meter is shown as the maximum and the Orc has sustained injuries!

Hmm, the Orc thinks he can defeat Sir Foo! This is interesting. Let's develop the game first and then see who has a better chance of winning!

With this understanding of the problem, let's review the code that implements this feature.

Reviewing the code

Download the source file, ch01_ex02.py, from the chapter's code bundle and skim through the code. The key logic will be in the attack() function. We will also need a data structure to keep the health record of Sir Foo and the enemy. Let's start by introducing the following utility functions that take care of some print business:

Now, look at the main function, run_application, and the supporting function, reset_health_meter. In addition to introducing the dictionary health_meter, we have also encapsulated the game logic in play_game:

At the start of a new game, the values of the health_meter dictionary are set back to the initial ones by calling reset_health_meter:

Next, let's review the play_game function. If the hut has the enemy, the player will be asked if the attack should be continued (the start of the while loop). Based on the user input, the code calls the attack function or exits the current game:

The enemy is attacked repetitively using the interactive while loop, which accepts user input. Execution of the attack function may result in injury to Sir Foo, or the enemy, or both. It is also possible that no one gets hurt. For simplicity, we will only consider two possibilities: a single attack that will injure either the enemy or Sir Foo. In the previous section, we used the built-in random number generator to randomly determine the occupants of the huts. We can use the same technique to determine who gets hurt:

injured_unit = random.choice(['player', 'enemy'])

But hold on a minute. Sir Foo has something to say:

We should take into account the chance of an injury to the player and to the enemy. In the attack function shown next, we will assume that for about 60% of the time, the enemy will get hit and for the remaining 40%, it is Sir Foo who is on the receiving end.

The simplest way is to create a list with 10 elements. This list should have six entries of 'enemy' and four entries of 'player'. Then, let random.choice select an element from this list. You can always introduce a difficulty level in the game and change this distribution:

Once the injured_unit is selected randomly, the injury is determined by picking a random number between 10 and 15, inclusive. Here, we use the random.randint function. The final important step is to update the health_meter dictionary for the injured unit by reducing its number of hit points.

Running Attack of the Orcs v0.0.5

We have discussed the most important functions in this game. Review the other supporting functions from the downloaded file. The following screenshot shows the game in action: