- Structural Planning
When working on a large project, it helps to plan out how you'll organize your code before actually writing the code. This avoids time-consuming refactoring later down the road.
First, think about how you can separate out the different elements of your project. Is there anything that could be naturally organized as a class/object, or a group of functions? Which variables and functions should be stored where? Would inheritance or well-defined helper functions be useful anywhere for reducing code repitition? Being able to turn a set of needs into an organizational structure is the extremely important in large projects!
As an example, assume you want to build a computer game that has three types of characters: players, friends, and foes. All characters have a name and a location and can be moved to new locations. Players and foes have health levels and can attack other characters. A friend can defend a player but can neither attack nor be hurt by an attack from another character. A player can be moved directly by the user. From this prompt, we can generate the following code structure:
# object Location
# attribute name
#
# object Character
# attribute name
# attribute location
#
# object Fighter inherits from Character
# attribute health level
# function attack(Character)
#
# object Player inherits from Fighter
# function move(Location)
#
# object Foe inherits from Fighter
#
# object Friend inherits from Character
# function defend(Character)
Once the goals of a project have been separated into different objects and groups of functions, they can be moved into individual files, which can then be imported and called from each other. In Python, the main file is traditionally called __init__.py, while other files are named based on their purpose. In general, each object definition should have its own file, unless several objects are directly related.
Each file you create should be properly documented with a comment at the top explaining what that file does. In particularly large projects, you may also want to organize your files into directories based on their different purposes. Files in different directories can also be imported from each other! An example of a project file structure can be found here.
# The main file, __init__.py
# This file runs the whole game by calling playGame
# In this directory, we have a file named heroClass.py, a file named monster.py, and a folder named gui which contains the file displayGame.py. We also have a folder named images where all assets are stored.
import heroClass
from monster import *
from gui import displayGame
def playGame():
# When we import a file normally, we have to reference that file name before
# the functions/classes in it
mainPlayer = heroClass.Hero("Fred")
# When we use from file import *, all the functions/classes from that file
# get imported directly into our current namespace!
monsters = spawnMonsters()
# We can also use from directory import file to organize files nicely
displayGame.runGame(mainPlayer, monsters)
- Algorithmic Planning
While planning, it also makes sense to start by identifying what the most algorithmically complex parts of the project will be. This can help you write out approaches for algorithms in pseudocode early on, so that you aren't stuck trying to code a complex algorithm from scratch near the deadline. Algorithmic plans can be fairly high-level, as they will mainly serve as a roadmap for later work. An example of a high-level plan is shown below:
#########################################################
# Algorithmic plan for populating the field with monsters
#########################################################
#
# First: generate a set of monsters with varying attack levels
# - number of monsters should be determined by field size
# - level should be determined by hero level at beginning: half should be at
# hero level, 1/4 one level above, 1/4 two levels above, and 1 five levels above
# - distinguish monsters based on fill color?
#
# Second: determine where monsters should be placed on the field
# - use random library to randomly place them
# - but weight the placement so that easier monsters mostly appear closer to the hero
# - non-randomly place the boss monster at the end
#
# Third: don't start monster movement until the gameplay starts
# - have a class method to pause/unpause all monsters? or put a block in timerFired?
#########################################################
In general, when planning algorithms early on, it's a good idea to use the problem-solving procedures we discussed earlier in the semester. This will help you tackle the difficult problems in your code.