Search icon
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletters
Free Learning
Arrow right icon
Learning Python Application Development

You're reading from  Learning Python Application Development

Product type Book
Published in Sep 2016
Publisher Packt
ISBN-13 9781785889196
Pages 454 pages
Edition 1st Edition
Languages
Author (1):
Ninad Sathaye Ninad Sathaye
Profile icon Ninad Sathaye

Table of Contents (18) Chapters

Learning Python Application Development
Credits
Disclaimers
About the Author
About the Reviewer
www.PacktPub.com
Preface
1. Developing Simple Applications 2. Dealing with Exceptions 3. Modularize, Package, Deploy! 4. Documentation and Best Practices 5. Unit Testing and Refactoring 6. Design Patterns 7. Performance – Identifying Bottlenecks 8. Improving Performance – Part One 9. Improving Performance – Part Two, NumPy and Parallelization 10. Simple GUI Applications 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:

You have been reading a chapter from
Learning Python Application Development
Published in: Sep 2016 Publisher: Packt ISBN-13: 9781785889196
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime}