├── .gitignore
├── LICENSE
├── README.md
├── asset
├── arrowheads-processed.psd
├── arrowheads-scan.psd
├── object-pool-heap-fragment.psd
├── process.txt
├── style.scss
├── subclass-sandbox-coupling.psd
├── template.html
└── template.xml
├── book
├── acknowledgements.markdown
├── architecture-performance-and-games.markdown
├── behavioral-patterns.markdown
├── bytecode.markdown
├── command.markdown
├── component.markdown
├── data-locality.markdown
├── decoupling-patterns.markdown
├── design-patterns-revisited.markdown
├── dirty-flag.markdown
├── double-buffer.markdown
├── event-queue.markdown
├── flyweight.markdown
├── game-loop.markdown
├── introduction.markdown
├── object-pool.markdown
├── observer.markdown
├── optimization-patterns.markdown
├── prototype.markdown
├── sequencing-patterns.markdown
├── service-locator.markdown
├── singleton.markdown
├── spatial-partition.markdown
├── state.markdown
├── subclass-sandbox.markdown
├── type-object.markdown
└── update-method.markdown
├── build
├── code
├── cpp
│ ├── bytecode.h
│ ├── command.h
│ ├── common.h
│ ├── component.h
│ ├── cpp.xcodeproj
│ │ └── project.pbxproj
│ ├── data-locality.h
│ ├── dirty-flag.h
│ ├── double-buffer.h
│ ├── event-queue.h
│ ├── expect.h
│ ├── flyweight.h
│ ├── game-loop.h
│ ├── hello-world.h
│ ├── introduction.h
│ ├── main.cpp
│ ├── object-pool.h
│ ├── observer.h
│ ├── prototype.h
│ ├── service-locator.h
│ ├── singleton.h
│ ├── spatial-partition.h
│ ├── state.h
│ ├── subclass-sandbox.h
│ ├── type-object.h
│ └── update-method.h
├── dart
│ └── spatial-partition
│ │ └── pigeonhole_comparisons.dart
├── flyweight
│ └── tiles
│ │ ├── tiles.xcodeproj
│ │ └── project.pbxproj
│ │ └── tiles
│ │ ├── main.cpp
│ │ └── tiles.1
└── structure-of-arrays
│ ├── activate
│ ├── activate.xcodeproj
│ │ └── project.pbxproj
│ └── main.cpp
│ ├── combined
│ ├── combined.xcodeproj
│ │ └── project.pbxproj
│ └── main.cpp
│ ├── component_update
│ ├── component_update.xcodeproj
│ │ └── project.pbxproj
│ └── main.cpp
│ ├── is_active
│ ├── is_active.xcodeproj
│ │ └── project.pbxproj
│ └── main.cpp
│ ├── random_array_access
│ ├── main.cpp
│ └── random_array_access.xcodeproj
│ │ └── project.pbxproj
│ └── shared
│ └── utils.h
├── draft
├── first draft
│ ├── game-loop.markdown
│ ├── introduction.markdown
│ ├── object-pool.markdown
│ ├── service-locator.markdown
│ ├── singleton.markdown
│ ├── spatial-partition.markdown
│ └── update-method.markdown
└── outline
│ ├── context-parameter.markdown
│ ├── double-buffer.markdown
│ ├── game-loop.markdown
│ ├── hello-world.markdown
│ ├── introduction.markdown
│ ├── service.markdown
│ ├── spatial-partition.markdown
│ ├── subclass-sandbox.markdown
│ ├── type-object.markdown
│ └── update-method.markdown
├── html
├── acknowledgements.html
├── architecture-performance-and-games.html
├── behavioral-patterns.html
├── bytecode.html
├── command.html
├── component.html
├── data-locality.html
├── decoupling-patterns.html
├── design-patterns-revisited.html
├── dirty-flag.html
├── double-buffer.html
├── event-queue.html
├── flyweight.html
├── game-loop.html
├── images
│ ├── Thumbs.db
│ ├── architecture-cycle.png
│ ├── arrow-inherits.png
│ ├── arrow-references.png
│ ├── background.png
│ ├── bytecode-ast.png
│ ├── bytecode-code.png
│ ├── bytecode-literal.png
│ ├── bytecode-numbers.png
│ ├── bytecode-stack-1.png
│ ├── bytecode-stack-2.png
│ ├── bytecode-stack-3.png
│ ├── bytecode-ui.png
│ ├── command-buttons-one.png
│ ├── command-buttons-two.png
│ ├── command-stream.png
│ ├── command-undo.png
│ ├── component-uml.png
│ ├── data-locality-cache-line.png
│ ├── data-locality-chart.png
│ ├── data-locality-component-arrays.png
│ ├── data-locality-pointer-chasing.png
│ ├── data-locality-things.png
│ ├── dirty-flag-multiply.png
│ ├── dirty-flag-pirate.png
│ ├── dirty-flag-title-bar.png
│ ├── dirty-flag-update-bad.png
│ ├── dirty-flag-update-good.png
│ ├── dogshot.jpg
│ ├── double-buffer-face.png
│ ├── double-buffer-slaps-1.png
│ ├── double-buffer-slaps-2.png
│ ├── double-buffer-tearing.png
│ ├── event-queue-central.png
│ ├── event-queue-crawl.png
│ ├── event-queue-loop.png
│ ├── event-queue-queue.png
│ ├── event-queue-ring.png
│ ├── flyweight-tiles.png
│ ├── flyweight-tree-model.png
│ ├── flyweight-trees.png
│ ├── game-loop-fixed.png
│ ├── game-loop-simple.png
│ ├── game-loop-timeline-close.png
│ ├── game-loop-timeline.png
│ ├── object-pool-heap-fragment.png
│ ├── observer-linked.png
│ ├── observer-list.png
│ ├── observer-nodes.png
│ ├── observer-weasel-wielder.png
│ ├── prototype-class.png
│ ├── prototype-delegate.png
│ ├── prototype-hierarchies.png
│ ├── prototype-object.png
│ ├── prototype-spawner.png
│ ├── prototype-weapon.png
│ ├── spatial-partition-adjacent.png
│ ├── spatial-partition-battle-line.png
│ ├── spatial-partition-grid.png
│ ├── spatial-partition-linked-list.png
│ ├── spatial-partition-neighbors.png
│ ├── spatial-partition-quadtree.png
│ ├── state-flowchart.png
│ ├── state-pushdown.png
│ ├── type-object-breed.png
│ ├── type-object-subclasses.png
│ ├── update-method-remove.png
│ └── update-method-uml.png
├── index.html
├── introduction.html
├── jquery-3.6.0.min.js
├── object-pool.html
├── observer.html
├── optimization-patterns.html
├── prototype.html
├── script.js
├── sequencing-patterns.html
├── service-locator.html
├── singleton.html
├── spatial-partition.html
├── state.html
├── style.css
├── style.css.map
├── subclass-sandbox.html
├── type-object.html
└── update-method.html
├── note
├── bio.txt
├── description.txt
├── dimensions.txt
├── fonts.txt
├── log.txt
├── print workflow.txt
├── publishing.txt
├── references.txt
├── style.markdown
└── todo.txt
├── script
└── format.py
└── watch
/.gitignore:
--------------------------------------------------------------------------------
1 | code/cpp/build/
2 | code/flyweight/tiles/Build/
3 | .sass-cache/
4 | xcuserdata/
5 | *.xcuserstate
6 | project.xcworkspace/
7 |
8 | xml/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2009 Robert Nystrom
2 |
3 | ------------------------ Commentary ------------------------
4 |
5 | The licensing story for this repository is a little complex.
6 | Here's my motivation:
7 |
8 | * I want you to get as much use out of the material here as
9 | possible. I wrote this book to help you, and I don't want
10 | you to be encumbered when it comes to making the most of
11 | it. That's also why I put it online for free.
12 |
13 | * I have been immensely fortunate to have a large number of
14 | people contribute to the book through bug reports, fixes,
15 | pull requests, and other suggestions. Putting on GitHub
16 | and treating it like an open source project made a
17 | dramatic improvement in the resulting quality of the
18 | book.
19 |
20 | I want to ensure people can fork the repo, send me fixes,
21 | etc. without violating the license or feeling weird.
22 |
23 | * When it comes to code, I'm completely comfortable with
24 | people redistributing, remixing, changing, whatever with
25 | it. I've been using the MIT license for open source stuff
26 | for decades.
27 |
28 | * When it comes to my prose and the visual design of the
29 | site, that feels a little more, I don't know, *me* than
30 | the code. The words are in my voice, and the look of the
31 | site is part of its and, by extension, my brand.
32 |
33 | I feel weird thinking about someone, say taking one of the
34 | chapters and making significant changes to it to fit their
35 | writing style while still having some of it read like it
36 | came from me. Likewise, I'd be sad to see another site
37 | online that looked exactly like mine because it reuses my
38 | stylesheets.
39 |
40 | * Because -- and this came as a total surprise to me --
41 | there are foreign publishers interested in the
42 | translation rights to the book, I want to be careful to
43 | not be so permissive that it prevents me from signing
44 | typical contracts that give them exclusive translation
45 | rights to certain territories and languages.
46 |
47 | * If I allow the prose to be redistributed commercially,
48 | there is nothing preventing someone from slapping
49 | together a cheap print or ebook version of the book and
50 | putting it up for sale. I'm not too worried about my own
51 | sales being undercut, but I very much want to avoid
52 | readers finding themselves with a low quality book that
53 | they incorrectly think is from me.
54 |
55 | I worked very hard on this book, including type setting,
56 | layout, generating the ePub file, styling it, etc. I want
57 | you to get the best possible experience.
58 |
59 | All of this is way more complex than I'd like, especially
60 | since my brain isn't wired to care about intellectual
61 | property. I like thinking about making stuff, not thinking
62 | about the legal rights around the stuff I made. (If your
63 | brain is wired to think about legal stuff and you see that
64 | I'm doing something dumb, please do let me know.)
65 |
66 | The best solution I've been able to come up with is to use
67 | two licenses:
68 |
69 | ------------------------ License(s) ------------------------
70 |
71 | Each file in this repository falls under one of two
72 | licenses. Files whose extension is ".html", ".scss", ".css",
73 | ".markdown", or ".txt" use this Creative Commons license:
74 |
75 | Attribution-NonCommercial-NoDerivatives 4.0
76 | International (CC BY-NC-ND 4.0)
77 |
78 | https://creativecommons.org/licenses/by-nc-nd/4.0/
79 |
80 | All other files, including (but not limited to) ".py", "h",
81 | and ".cpp" use the MIT license:
82 |
83 | Permission is hereby granted, free of charge, to any
84 | person obtaining a copy of this software and associated
85 | documentation files (the "Software"), to deal in the
86 | Software without restriction, including without
87 | limitation the rights to use, copy, modify, merge,
88 | publish, distribute, sublicense, and/or sell copies of
89 | the Software, and to permit persons to whom the Software
90 | is furnished to do so, subject to the following
91 | conditions:
92 |
93 | The above copyright notice and this permission notice
94 | shall be included in all copies or substantial portions
95 | of the Software.
96 |
97 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
98 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
99 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
100 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
101 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
102 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
103 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
104 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
105 | DEALINGS IN THE SOFTWARE.
106 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | **Note: Now that the book is done, I'm not actively working on it.**
2 |
3 | There are only so many hours in the day, and I have other projects that need my
4 | love, including a new book. This means I'm not responding to issues and pull
5 | requests here. However, I do intend to circle back and do a second edition at
6 | *some* point, so please do continue to file bugs.
7 |
8 | Even though I'm not responding, I do deeply appreciate every issue filed.
9 | Thank you!
10 |
11 | – bob
12 |
13 | ---
14 |
15 | This is the source repo for the book [Game Programming Patterns][].
16 |
17 | ## Building the Book
18 |
19 | The book is written in Markdown (in `book/`). A little Python script (`script/format.py`) converts that along with a SASS file (`asset/style.scss`) and HTML template (`asset/template.html`) to the final HTML (in `html/`). To run the format script locally, you'll need to have Python 2.7-ish, and install Python Markdown, Pygments, and SmartyPants:
20 |
21 | $ pip install markdown
22 | $ pip install pygments
23 | $ pip install smartypants
24 |
25 | You may need `sudo` for those. Once that's done, you can run:
26 |
27 | $ python script/format.py
28 |
29 | Make sure to run this from the root directory of the repo. That will regenerate all of the chapter and section intro HTML files. If you're editing stuff, the script can also be run in watch mode:
30 |
31 | $ python script/format.py --watch
32 |
33 | That will monitor the file system for changes to the markdown files, SASS file, or HTML template, and reprocess them as needed.
34 |
35 | [game programming patterns]: http://gameprogrammingpatterns.com/
36 |
--------------------------------------------------------------------------------
/asset/arrowheads-processed.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/asset/arrowheads-processed.psd
--------------------------------------------------------------------------------
/asset/arrowheads-scan.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/asset/arrowheads-scan.psd
--------------------------------------------------------------------------------
/asset/object-pool-heap-fragment.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/asset/object-pool-heap-fragment.psd
--------------------------------------------------------------------------------
/asset/process.txt:
--------------------------------------------------------------------------------
1 | 1. Desaturate.
2 | 2. Adjust the bottom end of the levels a bit to get the darkest to just be black (but no other grays).
3 | 3. Unsharp mask 90% 3.5 px 7 threshold
4 | 4. Now adjust the levels again to make the pen lines mostly solid black. Bring up the mids a bit so the grid lines aren't too dark.
5 | 5. Rotate about 1° CCW.
6 | 6. Add the yellow overlay.
7 | 7. Scale and crop to 1040 wide.
8 | 8. Save as 8-bit PNG with diffusion dither and 32 colors.
--------------------------------------------------------------------------------
/asset/subclass-sandbox-coupling.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/asset/subclass-sandbox-coupling.psd
--------------------------------------------------------------------------------
/asset/template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{title}} · Game Programming Patterns
7 |
8 |
10 |
11 |
12 |
13 |
22 |
23 |
24 |
25 |
26 |
43 | © 2009-2021 Robert Nystrom
44 |
45 |
46 |
--------------------------------------------------------------------------------
/book/acknowledgements.markdown:
--------------------------------------------------------------------------------
1 | ^title Acknowledgements
2 |
3 | I've heard only other authors know what's involved in writing a book, but there
4 | is another tribe who know the precise weight of that burden -- those with the
5 | misfortune of being in a relationship with a writer. I wrote this in a space of
6 | time painstakingly carved from the dense rock of life for me by my wife Megan.
7 | Washing dishes and giving the kids baths may not be "writing", but without her
8 | doing those, this book wouldn't be here.
9 |
10 | I started this project while a programmer at Electronic Arts. I don't think the
11 | company knew quite what to make of it, and I'm grateful to Michael Malone,
12 | Olivier Nallet, and Richard Wifall for supporting it and providing detailed,
13 | insightful feedback on the first few chapters.
14 |
15 | About halfway through writing, I decided to forgo a traditional publisher. I
16 | knew that meant losing the guidance an editor brings, but I had email from
17 | dozens of readers telling me where they wanted the book to go. I'd lose
18 | proofreaders, but I had over 250 bug reports to help improve the prose. I'd give
19 | up the incentive of a writing schedule, but with readers patting my back when I
20 | finished each chapter, I had more than enough motivation.
21 |
22 |
23 |
24 | What I didn't lose was a copy editor. Lauren Briese showed up just when I
25 | needed her and did a wonderful job.
26 |
27 |
28 |
29 | They call this "self publishing", but "crowd publishing" is closer to the mark.
30 | Writing can be lonely work, but I was never alone. Even when I put the book on a
31 | shelf for two years, the encouragement continued. Without the dozens of people
32 | who didn't let me forget that they were waiting for more chapters, I never would
33 | have picked it back up and finished.
34 |
35 |
36 |
37 | Special thanks go to Colm Sloan who pored over every single chapter in the book
38 | *twice* and gave me mountains of fantastic feedback, all out of the goodness of
39 | his own heart. I owe you a beer or twenty.
40 |
41 |
42 |
43 | To everyone who emailed or commented, upvoted or favorited, tweeted or
44 | retweeted, anyone who reached out to me, or told a friend about the book, or
45 | sent me a bug report: my heart is filled with gratitude for you. Completing this
46 | book was one of my biggest goals in life, and you made it happen.
47 |
48 | Thank you!
49 |
--------------------------------------------------------------------------------
/book/behavioral-patterns.markdown:
--------------------------------------------------------------------------------
1 | ^title Behavioral Patterns
2 |
3 | Once you've built your game's set and festooned it with actors and props, all
4 | that remains is to start the scene. For this, you need behavior -- the
5 | screenplay that tells each entity in your game what to do.
6 |
7 | Of course all code is "behavior", and all software is defining behavior, but
8 | what's different about games is often the *breadth* of it that you have to
9 | implement. While your word processor may have a long list of features, it pales
10 | in comparison with the number of inhabitants, items, and quests in your average
11 | role-playing game.
12 |
13 | The patterns in this chapter help to quickly define and refine a large quantity of
14 | maintainable behavior. [Type Objects](type-object.html) create
15 | categories of behavior without the rigidity of defining an actual class. A
16 | [Subclass Sandbox](subclass-sandbox.html) gives you a safe set of primitives
17 | you can use to define a variety of behaviors. The most advanced option is
18 | [Bytecode](bytecode.html), which moves behavior out of code entirely and into
19 | data.
20 |
21 | ## The Patterns
22 |
23 | * [Bytecode](bytecode.html)
24 | * [Subclass Sandbox](subclass-sandbox.html)
25 | * [Type Object](type-object.html)
26 |
--------------------------------------------------------------------------------
/book/decoupling-patterns.markdown:
--------------------------------------------------------------------------------
1 | ^title Decoupling Patterns
2 |
3 | Once you get the hang of a programming language, writing code to do what you
4 | want is actually pretty easy. What's hard is writing code that's easy to adapt
5 | when your requirements *change*. Rarely do we have the luxury of a perfect
6 | feature set before we've fired up our editor.
7 |
8 | A powerful tool we have for making change easier is *decoupling*. When we say
9 | two pieces of code are "decoupled", we mean a change in one usually doesn't
10 | require a change in the other. When you change some feature in your game, the
11 | fewer places in code you have to touch, the easier it is.
12 |
13 | [Components](component.html) decouple different domains in your game from each
14 | other within a single entity that has aspects of all of them. [Event
15 | Queues](event-queue.html) decouple two objects communicating with each other,
16 | both statically and *in time*. [Service Locators](service-locator.html) let
17 | code access a facility without being bound to the code that provides it.
18 |
19 | ## The Patterns
20 |
21 | * [Component](component.html)
22 | * [Event Queue](event-queue.html)
23 | * [Service Locator](service-locator.html)
24 |
--------------------------------------------------------------------------------
/book/design-patterns-revisited.markdown:
--------------------------------------------------------------------------------
1 | ^title Design Patterns Revisited
2 |
3 | *Design Patterns: Elements of Reusable Object-Oriented Software* is nearly
4 | twenty years old by my watch. Unless you're looking over my shoulder, there's a
5 | good chance *Design Patterns* will be old enough to drink by the time you read
6 | this. For an industry as quickly moving as software, that's practically ancient.
7 | The enduring popularity of the book says something about how timeless design
8 | is compared to many frameworks and methodologies.
9 |
10 | While I think *Design Patterns* is still relevant, we've learned a lot in the
11 | past couple of decades. In this section, we'll walk through a handful of the
12 | original patterns the Gang of Four documented. For each pattern, I hope to have
13 | something useful or interesting to say.
14 |
15 | I think some patterns are overused (Singleton ),
16 | while others are underappreciated (Command ). A couple
17 | are in here because I want to explore their relevance specifically to games (Flyweight and Observer ).
19 | Finally, sometimes I just think it's fun to see how patterns are enmeshed in
20 | the larger field of programming (Prototype and State ).
22 |
23 | ## The Patterns
24 |
25 | * [Command](command.html)
26 | * [Flyweight](flyweight.html)
27 | * [Observer](observer.html)
28 | * [Prototype](prototype.html)
29 | * [Singleton](singleton.html)
30 | * [State](state.html)
31 |
--------------------------------------------------------------------------------
/book/optimization-patterns.markdown:
--------------------------------------------------------------------------------
1 | ^title Optimization Patterns
2 |
3 | While the rising tide of faster and faster hardware has lifted most software
4 | above worrying about performance, games are one of the few remaining exceptions.
5 | Players always want richer, more realistic and exciting experiences. Screens are
6 | crowded with games vying for a player's attention -- and cash! -- and the game
7 | that pushes the hardware the furthest often wins.
8 |
9 | Optimizing for performance is a deep art that touches all aspects of software.
10 | Low-level coders master the myriad idiosyncrasies of hardware architectures.
11 | Meanwhile, algorithms researchers compete to prove mathematically whose
12 | procedure is the most efficient.
13 |
14 | Here, I touch on a few mid-level patterns that are often used to speed up a
15 | game. [Data Locality](data-locality.html) introduces you to the modern
16 | computer's memory hierarchy and how you can use it to your advantage. The [Dirty
17 | Flag](dirty-flag.html) pattern helps you avoid unnecessary computation while
18 | [Object Pools](object-pool.html) help you avoid unnecessary allocation. [Spatial
19 | Partitioning](spatial-partition.html) speeds up the virtual world and its
20 | inhabitants' arrangement in space.
21 |
22 | ## The Patterns
23 |
24 | * [Data Locality](data-locality.html)
25 | * [Dirty Flag](dirty-flag.html)
26 | * [Object Pool](object-pool.html)
27 | * [Spatial Partition](spatial-partition.html)
28 |
--------------------------------------------------------------------------------
/book/sequencing-patterns.markdown:
--------------------------------------------------------------------------------
1 | ^title Sequencing Patterns
2 |
3 | Videogames are exciting in large part because they take us somewhere else. For a
4 | few minutes (or, let's be honest with ourselves, much longer), we become
5 | inhabitants of a virtual world. Creating these worlds is one of the supreme
6 | delights of being a game programmer.
7 |
8 | One aspect that most of these game worlds feature is *time* -- the artificial
9 | world lives and breathes at its own cadence. As world builders, we must invent
10 | time and craft the gears that drive our game's great clock.
11 |
12 | The patterns in this section are tools for doing just that. A [Game
13 | Loop](game-loop.html) is the central axle that the clock spins on. Objects hear
14 | its ticking through [Update Methods](update-method.html). We can hide the
15 | computer's sequential nature behind a facade of snapshots of moments in time
16 | using [Double Buffering](double-buffer.html) so that the world appears to
17 | update simultaneously.
18 |
19 | ## The Patterns
20 |
21 | * [Double Buffer](double-buffer.html)
22 | * [Game Loop](game-loop.html)
23 | * [Update Method](update-method.html)
24 |
--------------------------------------------------------------------------------
/build:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | python3 script/format.py $@
3 |
--------------------------------------------------------------------------------
/code/cpp/command.h:
--------------------------------------------------------------------------------
1 | //
2 | // command.h
3 | // cpp
4 | //
5 | // Created by Bob Nystrom on 10/7/13.
6 | // Copyright (c) 2013 Bob Nystrom. All rights reserved.
7 | //
8 |
9 | #ifndef cpp_command_h
10 | #define cpp_command_h
11 |
12 | namespace CommandPattern
13 | {
14 | enum Button
15 | {
16 | BUTTON_UP,
17 | BUTTON_DOWN,
18 | BUTTON_LEFT,
19 | BUTTON_RIGHT,
20 | BUTTON_X,
21 | BUTTON_Y,
22 | BUTTON_A,
23 | BUTTON_B
24 | };
25 |
26 | bool isPressed(Button button) { return false; }
27 | void jump() {}
28 | void fireGun() {}
29 | void swapWeapon() {}
30 | void lurchIneffectively() {}
31 |
32 | namespace BeforeCommand
33 | {
34 | class InputHandler
35 | {
36 | public:
37 | void handleInput();
38 | };
39 |
40 | //^handle-input
41 | void InputHandler::handleInput()
42 | {
43 | if (isPressed(BUTTON_X)) jump();
44 | else if (isPressed(BUTTON_Y)) fireGun();
45 | else if (isPressed(BUTTON_A)) swapWeapon();
46 | else if (isPressed(BUTTON_B)) lurchIneffectively();
47 | }
48 | //^handle-input
49 | }
50 |
51 | namespace InputHandlingCommand
52 | {
53 | //^command
54 | class Command
55 | {
56 | public:
57 | virtual ~Command() {}
58 | virtual void execute() = 0;
59 | };
60 | //^command
61 |
62 | //^command-classes
63 | class JumpCommand : public Command
64 | {
65 | public:
66 | virtual void execute() { jump(); }
67 | };
68 |
69 | class FireCommand : public Command
70 | {
71 | public:
72 | virtual void execute() { fireGun(); }
73 | };
74 |
75 | // You get the idea...
76 | //^command-classes
77 |
78 | //^input-handler-class
79 | class InputHandler
80 | {
81 | public:
82 | void handleInput();
83 |
84 | // Methods to bind commands...
85 |
86 | private:
87 | Command* buttonX_;
88 | Command* buttonY_;
89 | Command* buttonA_;
90 | Command* buttonB_;
91 | };
92 | //^input-handler-class
93 |
94 | //^handle-input-commands
95 | void InputHandler::handleInput()
96 | {
97 | if (isPressed(BUTTON_X)) buttonX_->execute();
98 | else if (isPressed(BUTTON_Y)) buttonY_->execute();
99 | else if (isPressed(BUTTON_A)) buttonA_->execute();
100 | else if (isPressed(BUTTON_B)) buttonB_->execute();
101 | }
102 | //^handle-input-commands
103 | }
104 |
105 | namespace CommandedActors
106 | {
107 | class GameActor
108 | {
109 | public:
110 | void jump() {}
111 | };
112 |
113 | //^actor-command
114 | class Command
115 | {
116 | public:
117 | virtual ~Command() {}
118 | virtual void execute(GameActor& actor) = 0;
119 | };
120 | //^actor-command
121 |
122 | //^jump-actor
123 | class JumpCommand : public Command
124 | {
125 | public:
126 | virtual void execute(GameActor& actor)
127 | {
128 | actor.jump();
129 | }
130 | };
131 | //^jump-actor
132 |
133 | class InputHandler
134 | {
135 | public:
136 | Command* handleInput();
137 | private:
138 | Command* buttonX_;
139 | Command* buttonY_;
140 | Command* buttonA_;
141 | Command* buttonB_;
142 | };
143 |
144 | //^handle-input-return
145 | Command* InputHandler::handleInput()
146 | {
147 | if (isPressed(BUTTON_X)) return buttonX_;
148 | if (isPressed(BUTTON_Y)) return buttonY_;
149 | if (isPressed(BUTTON_A)) return buttonA_;
150 | if (isPressed(BUTTON_B)) return buttonB_;
151 |
152 | // Nothing pressed, so do nothing.
153 | return NULL;
154 | }
155 | //^handle-input-return
156 |
157 | void executeCommand()
158 | {
159 | InputHandler inputHandler;
160 | GameActor actor;
161 | //^call-actor-command
162 | Command* command = inputHandler.handleInput();
163 | if (command)
164 | {
165 | command->execute(actor);
166 | }
167 | //^call-actor-command
168 | use(actor);
169 | }
170 | }
171 |
172 | namespace Undo
173 | {
174 | class Unit {
175 | public:
176 | int x() { return 0; }
177 | int y() { return 0; }
178 |
179 | void moveTo(int x, int y) {}
180 | };
181 |
182 | namespace UndoBefore
183 | {
184 | class Command
185 | {
186 | public:
187 | virtual ~Command() {}
188 | virtual void execute() = 0;
189 | };
190 |
191 | //^move-unit
192 | class MoveUnitCommand : public Command
193 | {
194 | public:
195 | MoveUnitCommand(Unit* unit, int x, int y)
196 | : unit_(unit),
197 | x_(x),
198 | y_(y)
199 | {}
200 |
201 | virtual void execute()
202 | {
203 | unit_->moveTo(x_, y_);
204 | }
205 |
206 | private:
207 | Unit* unit_;
208 | int x_, y_;
209 | };
210 | //^move-unit
211 |
212 | Unit* getSelectedUnit() { return NULL; }
213 |
214 | //^get-move
215 | Command* handleInput()
216 | {
217 | Unit* unit = getSelectedUnit();
218 |
219 | if (isPressed(BUTTON_UP)) {
220 | // Move the unit up one.
221 | int destY = unit->y() - 1;
222 | return new MoveUnitCommand(unit, unit->x(), destY);
223 | }
224 |
225 | if (isPressed(BUTTON_DOWN)) {
226 | // Move the unit down one.
227 | int destY = unit->y() + 1;
228 | return new MoveUnitCommand(unit, unit->x(), destY);
229 | }
230 |
231 | // Other moves...
232 |
233 | return NULL;
234 | }
235 | //^get-move
236 | }
237 |
238 | namespace UndoAfter
239 | {
240 | //^undo-command
241 | class Command
242 | {
243 | public:
244 | virtual ~Command() {}
245 | virtual void execute() = 0;
246 | virtual void undo() = 0;
247 | };
248 | //^undo-command
249 |
250 | //^undo-move-unit
251 | class MoveUnitCommand : public Command
252 | {
253 | public:
254 | MoveUnitCommand(Unit* unit, int x, int y)
255 | : unit_(unit),
256 | xBefore_(0),
257 | yBefore_(0),
258 | x_(x),
259 | y_(y)
260 | {}
261 |
262 | virtual void execute()
263 | {
264 | // Remember the unit's position before the move
265 | // so we can restore it.
266 | xBefore_ = unit_->x();
267 | yBefore_ = unit_->y();
268 |
269 | unit_->moveTo(x_, y_);
270 | }
271 |
272 | virtual void undo()
273 | {
274 | unit_->moveTo(xBefore_, yBefore_);
275 | }
276 |
277 | private:
278 | Unit* unit_;
279 | int xBefore_, yBefore_;
280 | int x_, y_;
281 | };
282 | //^undo-move-unit
283 | }
284 | }
285 | }
286 |
287 | #endif
288 |
--------------------------------------------------------------------------------
/code/cpp/common.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | // Makes the argument appear to be used so that we don't get an unused
4 | // variable warning for it. Lets us leave that warning on to catch unintended
5 | // unused variables.
6 | template
7 | void use(const T& obj) {
8 | // Do nothing.
9 | }
10 |
11 | #define ASSERT(condition) \
12 | if (!(condition)) \
13 | { \
14 | std::cout << "FAIL: " #condition << "\n" << __FILE__ \
15 | << ":" << __LINE__ << std::endl; \
16 | abort(); \
17 | }
18 |
19 | void assert(bool condition) {
20 | if (!condition) {
21 | printf("WTF\n");
22 | exit(1);
23 | }
24 | }
--------------------------------------------------------------------------------
/code/cpp/dirty-flag.h:
--------------------------------------------------------------------------------
1 | //
2 | // dirty-flag.h
3 | // cpp
4 | //
5 | // Created by Bob Nystrom on 9/5/13.
6 | // Copyright (c) 2013 Bob Nystrom. All rights reserved.
7 | //
8 |
9 | #ifndef cpp_dirty_flag_h
10 | #define cpp_dirty_flag_h
11 |
12 | namespace DirtyFlag
13 | {
14 | //^transform
15 | class Transform
16 | {
17 | public:
18 | static Transform origin();
19 |
20 | Transform combine(Transform& other);
21 | };
22 | //^transform
23 |
24 | Transform Transform::origin()
25 | {
26 | return Transform();
27 | }
28 |
29 | Transform Transform::combine(Transform& other)
30 | {
31 | return other;
32 | }
33 |
34 | class Mesh;
35 | static const int MAX_CHILDREN = 16;
36 |
37 | namespace Basic
38 | {
39 | //^graph-node
40 | class GraphNode
41 | {
42 | public:
43 | GraphNode(Mesh* mesh)
44 | : mesh_(mesh),
45 | local_(Transform::origin())
46 | {}
47 |
48 | private:
49 | Transform local_;
50 | Mesh* mesh_;
51 |
52 | GraphNode* children_[MAX_CHILDREN];
53 | int numChildren_;
54 | };
55 | //^graph-node
56 |
57 | void root()
58 | {
59 | //^scene-graph
60 | GraphNode* graph_ = new GraphNode(NULL);
61 | // Add children to root graph node...
62 | //^scene-graph
63 | use(graph_);
64 | }
65 | }
66 |
67 | //^render
68 | void renderMesh(Mesh* mesh, Transform transform);
69 | //^render
70 |
71 | void renderMesh(Mesh* mesh, Transform transform)
72 | {
73 |
74 | }
75 |
76 | namespace RenderOnTheFly
77 | {
78 | static const int MAX_CHILDREN = 16;
79 |
80 | class GraphNode
81 | {
82 | public:
83 | GraphNode(Mesh* mesh)
84 | : mesh_(mesh),
85 | local_(Transform::origin())
86 | {}
87 |
88 | void render(Transform parentWorld);
89 |
90 | private:
91 | Transform local_;
92 | Mesh* mesh_;
93 |
94 | GraphNode* children_[MAX_CHILDREN];
95 | int numChildren_;
96 | };
97 |
98 | //^render-on-fly
99 | void GraphNode::render(Transform parentWorld)
100 | {
101 | Transform world = local_.combine(parentWorld);
102 |
103 | if (mesh_) renderMesh(mesh_, world);
104 |
105 | for (int i = 0; i < numChildren_; i++)
106 | {
107 | children_[i]->render(world);
108 | }
109 | }
110 | //^render-on-fly
111 |
112 | void root()
113 | {
114 | GraphNode* graph_ = new GraphNode(NULL);
115 | //^render-root
116 | graph_->render(Transform::origin());
117 | //^render-root
118 | }
119 | }
120 |
121 | namespace Dirty
122 | {
123 | //^dirty-graph-node
124 | class GraphNode
125 | {
126 | public:
127 | GraphNode(Mesh* mesh)
128 | : mesh_(mesh),
129 | local_(Transform::origin()),
130 | dirty_(true)
131 | {}
132 | //^omit
133 | void setTransform(Transform local);
134 | void render(Transform parentWorld, bool dirty);
135 | //^omit
136 |
137 | // Other methods...
138 |
139 | private:
140 | Transform world_;
141 | bool dirty_;
142 | // Other fields...
143 | //^omit
144 | Transform local_;
145 | Mesh* mesh_;
146 |
147 | static const int MAX_CHILDREN = 16;
148 | GraphNode* children_[MAX_CHILDREN];
149 | int numChildren_;
150 | //^omit
151 | };
152 | //^dirty-graph-node
153 |
154 | //^set-transform
155 | void GraphNode::setTransform(Transform local)
156 | {
157 | local_ = local;
158 | dirty_ = true;
159 | }
160 | //^set-transform
161 |
162 | //^dirty-render
163 | void GraphNode::render(Transform parentWorld, bool dirty)
164 | {
165 | dirty |= dirty_;
166 | if (dirty)
167 | {
168 | world_ = local_.combine(parentWorld);
169 | dirty_ = false;
170 | }
171 |
172 | if (mesh_) renderMesh(mesh_, world_);
173 |
174 | for (int i = 0; i < numChildren_; i++)
175 | {
176 | children_[i]->render(world_, dirty);
177 | }
178 | }
179 | //^dirty-render
180 | }
181 | }
182 |
183 | #endif
184 |
--------------------------------------------------------------------------------
/code/cpp/double-buffer.h:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #define WHITE 0
4 | #define BLACK 1
5 |
6 | //^1
7 | class Framebuffer
8 | {
9 | public:
10 | Framebuffer() { clear(); }
11 |
12 | void clear()
13 | {
14 | for (int i = 0; i < WIDTH * HEIGHT; i++)
15 | {
16 | pixels_[i] = WHITE;
17 | }
18 | }
19 |
20 | void draw(int x, int y)
21 | {
22 | pixels_[(WIDTH * y) + x] = BLACK;
23 | }
24 |
25 | const char* getPixels()
26 | {
27 | return pixels_;
28 | }
29 |
30 | private:
31 | static const int WIDTH = 160;
32 | static const int HEIGHT = 120;
33 |
34 | char pixels_[WIDTH * HEIGHT];
35 | };
36 | //^1
37 |
38 | namespace Unbuffered
39 | {
40 | //^2
41 | class Scene
42 | {
43 | public:
44 | void draw()
45 | {
46 | buffer_.clear();
47 |
48 | buffer_.draw(1, 1);
49 | buffer_.draw(4, 1);
50 | buffer_.draw(1, 3);
51 | buffer_.draw(2, 4);
52 | buffer_.draw(3, 4);
53 | buffer_.draw(4, 3);
54 | }
55 |
56 | Framebuffer& getBuffer() { return buffer_; }
57 |
58 | private:
59 | Framebuffer buffer_;
60 | };
61 | //^2
62 |
63 | void InterruptMiddleOfDraw()
64 | {
65 | Framebuffer buffer_;
66 |
67 | //^3
68 | buffer_.draw(1, 1);
69 | buffer_.draw(4, 1);
70 | // <- Video driver reads pixels here!
71 | buffer_.draw(1, 3);
72 | buffer_.draw(2, 4);
73 | buffer_.draw(3, 4);
74 | buffer_.draw(4, 3);
75 | //^3
76 | }
77 | }
78 |
79 | namespace Buffered
80 | {
81 | //^4
82 | class Scene
83 | {
84 | public:
85 | Scene()
86 | : current_(&buffers_[0]),
87 | next_(&buffers_[1])
88 | {}
89 |
90 | void draw()
91 | {
92 | next_->clear();
93 |
94 | next_->draw(1, 1);
95 | // ...
96 | next_->draw(4, 3);
97 |
98 | swap();
99 | }
100 |
101 | Framebuffer& getBuffer() { return *current_; }
102 |
103 | private:
104 | void swap()
105 | {
106 | // Just switch the pointers.
107 | Framebuffer* temp = current_;
108 | current_ = next_;
109 | next_ = temp;
110 | }
111 |
112 | Framebuffer buffers_[2];
113 | Framebuffer* current_;
114 | Framebuffer* next_;
115 | };
116 | //^4
117 | }
118 |
119 | namespace UnbufferedSlapstick
120 | {
121 | class Stage;
122 |
123 | //^5
124 | class Actor
125 | {
126 | public:
127 | Actor() : slapped_(false) {}
128 |
129 | virtual ~Actor() {}
130 | virtual void update() = 0;
131 |
132 | void reset() { slapped_ = false; }
133 | void slap() { slapped_ = true; }
134 | bool wasSlapped() { return slapped_; }
135 |
136 | private:
137 | bool slapped_;
138 | };
139 | //^5
140 |
141 | //^6
142 | class Stage
143 | {
144 | public:
145 | void add(Actor* actor, int index)
146 | {
147 | actors_[index] = actor;
148 | }
149 |
150 | void update()
151 | {
152 | for (int i = 0; i < NUM_ACTORS; i++)
153 | {
154 | actors_[i]->update();
155 | actors_[i]->reset();
156 | }
157 | }
158 |
159 | private:
160 | static const int NUM_ACTORS = 3;
161 |
162 | Actor* actors_[NUM_ACTORS];
163 | };
164 | //^6
165 |
166 | //^7
167 | class Comedian : public Actor
168 | {
169 | public:
170 | //^omit
171 | Comedian() : name_("") {}
172 | Comedian(const char* name) : name_(name) {}
173 | //^omit
174 | void face(Actor* actor) { facing_ = actor; }
175 |
176 | virtual void update()
177 | {
178 | //^omit
179 | if (wasSlapped()) std::cout << name_ << " was slapped" << std::endl;
180 | //^omit
181 | if (wasSlapped()) facing_->slap();
182 | }
183 |
184 | private:
185 | //^omit
186 | const char* name_;
187 | //^omit
188 | Actor* facing_;
189 | };
190 | //^7
191 |
192 | void sample1()
193 | {
194 | //^8
195 | Stage stage;
196 |
197 | Comedian* harry = new Comedian();
198 | Comedian* baldy = new Comedian();
199 | Comedian* chump = new Comedian();
200 |
201 | harry->face(baldy);
202 | baldy->face(chump);
203 | chump->face(harry);
204 |
205 | stage.add(harry, 0);
206 | stage.add(baldy, 1);
207 | stage.add(chump, 2);
208 | //^8
209 |
210 | //^9
211 | harry->slap();
212 |
213 | stage.update();
214 | //^9
215 |
216 | //^10
217 | stage.add(harry, 2);
218 | stage.add(baldy, 1);
219 | stage.add(chump, 0);
220 | //^10
221 | }
222 |
223 | void testComedy1(int a, int b, int c)
224 | {
225 | std::cout << std::endl << "test" << std::endl;
226 |
227 | Stage stage;
228 |
229 | Comedian* larry = new Comedian("larry");
230 | Comedian* curly = new Comedian("curly");
231 | Comedian* shemp = new Comedian("shemp");
232 |
233 | larry->face(curly);
234 | curly->face(shemp);
235 | shemp->face(larry);
236 |
237 | stage.add(larry, a);
238 | stage.add(curly, b);
239 | stage.add(shemp, c);
240 |
241 | larry->slap();
242 | for (int i = 0; i < 3; i++)
243 | {
244 | std::cout << "update" << std::endl;
245 | stage.update();
246 | }
247 | }
248 |
249 | void testComedy()
250 | {
251 | testComedy1(0, 1, 2);
252 | testComedy1(2, 1, 0);
253 | }
254 | }
255 |
256 | namespace BufferedSlapstick
257 | {
258 | class Stage;
259 |
260 | //^11
261 | class Actor
262 | {
263 | public:
264 | Actor() : currentSlapped_(false) {}
265 |
266 | virtual ~Actor() {}
267 | virtual void update() = 0;
268 |
269 | void swap()
270 | {
271 | // Swap the buffer.
272 | currentSlapped_ = nextSlapped_;
273 |
274 | // Clear the new "next" buffer.
275 | nextSlapped_ = false;
276 | }
277 |
278 | void slap() { nextSlapped_ = true; }
279 | bool wasSlapped() { return currentSlapped_; }
280 |
281 | private:
282 | bool currentSlapped_;
283 | bool nextSlapped_;
284 | };
285 | //^11
286 |
287 | class Stage
288 | {
289 | public:
290 | void add(Actor* actor, int index) { actors_[index] = actor; }
291 | void update();
292 | private:
293 | static const int NUM_ACTORS = 3;
294 |
295 | Actor* actors_[NUM_ACTORS];
296 | };
297 |
298 | //^12
299 | void Stage::update()
300 | {
301 | for (int i = 0; i < NUM_ACTORS; i++)
302 | {
303 | actors_[i]->update();
304 | }
305 |
306 | for (int i = 0; i < NUM_ACTORS; i++)
307 | {
308 | actors_[i]->swap();
309 | }
310 | }
311 | //^12
312 |
313 | class Comedian : public Actor
314 | {
315 | public:
316 | //^omit
317 | Comedian() : name_("") {}
318 | Comedian(const char* name) : name_(name) {}
319 | //^omit
320 | void face(Actor* actor) { facing_ = actor; }
321 |
322 | virtual void update()
323 | {
324 | //^omit
325 | if (wasSlapped()) std::cout << name_ << " was slapped" << std::endl;
326 | //^omit
327 | if (wasSlapped()) facing_->slap();
328 | }
329 |
330 | private:
331 | //^omit
332 | const char* name_;
333 | //^omit
334 | Actor* facing_;
335 | };
336 |
337 | void sample1()
338 | {
339 | Stage stage;
340 |
341 | Comedian* harry = new Comedian();
342 | Comedian* baldy = new Comedian();
343 | Comedian* chump = new Comedian();
344 |
345 | harry->face(baldy);
346 | baldy->face(chump);
347 | chump->face(harry);
348 |
349 | stage.add(harry, 0);
350 | stage.add(baldy, 1);
351 | stage.add(chump, 2);
352 |
353 | harry->slap();
354 |
355 | stage.update();
356 | }
357 | }
358 |
359 | namespace SwapOffset
360 | {
361 | //^13
362 | class Actor
363 | {
364 | public:
365 | static void init() { current_ = 0; }
366 | static void swap() { current_ = next(); }
367 |
368 | void slap() { slapped_[next()] = true; }
369 | bool wasSlapped() { return slapped_[current_]; }
370 |
371 | private:
372 | static int current_;
373 | static int next() { return 1 - current_; }
374 |
375 | bool slapped_[2];
376 | };
377 | //^13
378 | }
379 |
--------------------------------------------------------------------------------
/code/cpp/expect.h:
--------------------------------------------------------------------------------
1 | //
2 | // expect.h
3 | // gpp
4 | //
5 | // Created by Bob Nystrom on 6/25/13.
6 | // Copyright (c) 2013 Bob Nystrom. All rights reserved.
7 | //
8 |
9 | #ifndef gpp_expect_h
10 | #define gpp_expect_h
11 |
12 | #define EXPECT(condition) \
13 | expect_(__FILE__, __LINE__, #condition, condition)
14 |
15 | static void expect_(const char * file, int line,
16 | const char * expression,
17 | bool condition)
18 | {
19 | using std::cout;
20 | using std::endl;
21 |
22 | if (condition)
23 | {
24 | cout << "PASS: " << expression << endl;
25 | }
26 | else
27 | {
28 | cout << "FAIL: " << expression << endl;
29 | cout << " " << file << ":" << line << endl;
30 | }
31 | }
32 |
33 | #endif
34 |
--------------------------------------------------------------------------------
/code/cpp/flyweight.h:
--------------------------------------------------------------------------------
1 | //
2 | // flyweight.h
3 | // cpp
4 | //
5 | // Created by Bob Nystrom on 9/21/13.
6 | // Copyright (c) 2013 Bob Nystrom. All rights reserved.
7 | //
8 |
9 | #ifndef cpp_flyweight_h
10 | #define cpp_flyweight_h
11 |
12 | namespace Flyweight
13 | {
14 | class Mesh {};
15 | class Skeleton {};
16 | class Texture {};
17 | class Pose {};
18 | class Vector {};
19 | class Color {};
20 |
21 | static const int WIDTH = 1024;
22 | static const int HEIGHT = 1024;
23 |
24 | static Texture GRASS_TEXTURE;
25 | static Texture HILL_TEXTURE;
26 | static Texture RIVER_TEXTURE;
27 |
28 | int random(int max) { return 0; }
29 |
30 | namespace HeavyTree
31 | {
32 | //^heavy-tree
33 | class Tree
34 | {
35 | private:
36 | Mesh mesh_;
37 | Texture bark_;
38 | Texture leaves_;
39 | Vector position_;
40 | double height_;
41 | double thickness_;
42 | Color barkTint_;
43 | Color leafTint_;
44 | };
45 | //^heavy-tree
46 | }
47 |
48 | namespace SplitTree
49 | {
50 | //^tree-model
51 | class TreeModel
52 | {
53 | private:
54 | Mesh mesh_;
55 | Texture bark_;
56 | Texture leaves_;
57 | };
58 | //^tree-model
59 |
60 | //^split-tree
61 | class Tree
62 | {
63 | private:
64 | TreeModel* model_;
65 |
66 | Vector position_;
67 | double height_;
68 | double thickness_;
69 | Color barkTint_;
70 | Color leafTint_;
71 | };
72 | //^split-tree
73 | }
74 |
75 | namespace TerrainEnum
76 | {
77 | //^terrain-enum
78 | enum Terrain
79 | {
80 | TERRAIN_GRASS,
81 | TERRAIN_HILL,
82 | TERRAIN_RIVER
83 | // Other terrains...
84 | };
85 | //^terrain-enum
86 |
87 | //^enum-world
88 | class World
89 | {
90 | private:
91 | Terrain tiles_[WIDTH][HEIGHT];
92 | //^omit
93 | int getMovementCost(int x, int y);
94 | bool isWater(int x, int y);
95 | //^omit
96 | };
97 | //^enum-world
98 |
99 | //^enum-data
100 | int World::getMovementCost(int x, int y)
101 | {
102 | switch (tiles_[x][y])
103 | {
104 | case TERRAIN_GRASS: return 1;
105 | case TERRAIN_HILL: return 3;
106 | case TERRAIN_RIVER: return 2;
107 | // Other terrains...
108 | }
109 | }
110 |
111 | bool World::isWater(int x, int y)
112 | {
113 | switch (tiles_[x][y])
114 | {
115 | case TERRAIN_GRASS: return false;
116 | case TERRAIN_HILL: return false;
117 | case TERRAIN_RIVER: return true;
118 | // Other terrains...
119 | }
120 | }
121 | //^enum-data
122 | }
123 |
124 | namespace TerrainClass
125 | {
126 | //^terrain-class
127 | class Terrain
128 | {
129 | public:
130 | Terrain(int movementCost,
131 | bool isWater,
132 | Texture texture)
133 | : movementCost_(movementCost),
134 | isWater_(isWater),
135 | texture_(texture)
136 | {}
137 |
138 | int getMovementCost() const { return movementCost_; }
139 | bool isWater() const { return isWater_; }
140 | const Texture& getTexture() const { return texture_; }
141 |
142 | private:
143 | int movementCost_;
144 | bool isWater_;
145 | Texture texture_;
146 | };
147 | //^terrain-class
148 |
149 | //^world-terrain-pointers
150 | class World
151 | {
152 | //^omit
153 | public:
154 | World()
155 | : grassTerrain_(1, false, GRASS_TEXTURE),
156 | hillTerrain_(3, false, HILL_TEXTURE),
157 | riverTerrain_(2, true, RIVER_TEXTURE)
158 | {}
159 | const Terrain& getTile(int x, int y) const;
160 | //^omit
161 | private:
162 | Terrain* tiles_[WIDTH][HEIGHT];
163 |
164 | // Other stuff...
165 | //^omit
166 | Terrain grassTerrain_;
167 | Terrain hillTerrain_;
168 | Terrain riverTerrain_;
169 | void generateTerrain();
170 | //^omit
171 | };
172 | //^world-terrain-pointers
173 |
174 | //^generate
175 | void World::generateTerrain()
176 | {
177 | // Fill the ground with grass.
178 | for (int x = 0; x < WIDTH; x++)
179 | {
180 | for (int y = 0; y < HEIGHT; y++)
181 | {
182 | // Sprinkle some hills.
183 | if (random(10) == 0)
184 | {
185 | tiles_[x][y] = &hillTerrain_;
186 | }
187 | else
188 | {
189 | tiles_[x][y] = &grassTerrain_;
190 | }
191 | }
192 | }
193 |
194 | // Lay a river.
195 | int x = random(WIDTH);
196 | for (int y = 0; y < HEIGHT; y++) {
197 | tiles_[x][y] = &riverTerrain_;
198 | }
199 | }
200 | //^generate
201 |
202 | //^get-tile
203 | const Terrain& World::getTile(int x, int y) const
204 | {
205 | return *tiles_[x][y];
206 | }
207 | //^get-tile
208 |
209 | void foo()
210 | {
211 | World world;
212 |
213 | //^use-get-tile
214 | int cost = world.getTile(2, 3).getMovementCost();
215 | //^use-get-tile
216 | use(cost);
217 | }
218 | }
219 |
220 | namespace WorldTerrain
221 | {
222 | class Terrain
223 | {
224 | public:
225 | Terrain(int movementCost,
226 | bool isWater,
227 | Texture texture)
228 | : movementCost_(movementCost),
229 | isWater_(isWater),
230 | texture_(texture)
231 | {}
232 |
233 | int getMovementCost() const { return movementCost_; }
234 | bool isWater() const { return isWater_; }
235 | const Texture& getTexture() const { return texture_; }
236 |
237 | private:
238 | int movementCost_;
239 | bool isWater_;
240 | Texture texture_;
241 | };
242 |
243 | //^world-terrain
244 | class World
245 | {
246 | public:
247 | World()
248 | : grassTerrain_(1, false, GRASS_TEXTURE),
249 | hillTerrain_(3, false, HILL_TEXTURE),
250 | riverTerrain_(2, true, RIVER_TEXTURE)
251 | {}
252 |
253 | private:
254 | Terrain grassTerrain_;
255 | Terrain hillTerrain_;
256 | Terrain riverTerrain_;
257 |
258 | // Other stuff...
259 | };
260 | //^world-terrain
261 | }
262 | }
263 |
264 | #endif
265 |
--------------------------------------------------------------------------------
/code/cpp/game-loop.h:
--------------------------------------------------------------------------------
1 | void processInput() {}
2 | void update() {}
3 | void update(double elapsed) {}
4 | void render() {}
5 | void render(double elapsed) {}
6 |
7 | double getCurrentTime() { return 0; }
8 | void sleep(double time) {}
9 |
10 | int FPS = 60;
11 | int MS_PER_FRAME = 1000 / FPS;
12 | int MS_PER_TICK = 1000 / FPS;
13 |
14 | namespace Repl
15 | {
16 | char* readCommand() { return NULL; }
17 | void handleCommand(char* command) {}
18 |
19 | void loop()
20 | {
21 | //^1
22 | while (true)
23 | {
24 | char* command = readCommand();
25 | handleCommand(command);
26 | }
27 | //^1
28 | }
29 | }
30 |
31 | namespace EventLoop
32 | {
33 | class Event {};
34 | Event* waitForEvent() { return NULL; }
35 | bool dispatchEvent(Event* event) { return false; }
36 |
37 | void loop()
38 | {
39 | //^2
40 | while (true)
41 | {
42 | Event* event = waitForEvent();
43 | dispatchEvent(event);
44 | }
45 | //^2
46 | }
47 | }
48 |
49 | namespace FastAsPossible
50 | {
51 | void runGame()
52 | {
53 | //^3
54 | while (true)
55 | {
56 | processInput();
57 | update();
58 | render();
59 | }
60 | //^3
61 | }
62 | }
63 |
64 | namespace FixedFramerate
65 | {
66 | void runGame()
67 | {
68 | //^4
69 | while (true)
70 | {
71 | double start = getCurrentTime();
72 | processInput();
73 | update();
74 | render();
75 |
76 | sleep(start + MS_PER_FRAME - getCurrentTime());
77 | }
78 | //^4
79 | }
80 | }
81 |
82 |
83 | namespace FluidFramerate
84 | {
85 | void runGame()
86 | {
87 | //^5
88 | double lastTime = getCurrentTime();
89 | while (true)
90 | {
91 | double current = getCurrentTime();
92 | double elapsed = current - lastTime;
93 | processInput();
94 | update(elapsed);
95 | render();
96 | lastTime = current;
97 | }
98 | //^5
99 | }
100 | }
101 |
102 | namespace FixedUpdateFramerate
103 | {
104 | void runGame()
105 | {
106 | const double MS_PER_UPDATE = 8;
107 |
108 | //^6
109 | double previous = getCurrentTime();
110 | double lag = 0.0;
111 | while (true)
112 | {
113 | double current = getCurrentTime();
114 | double elapsed = current - previous;
115 | previous = current;
116 | lag += elapsed;
117 |
118 | processInput();
119 |
120 | while (lag >= MS_PER_UPDATE)
121 | {
122 | update();
123 | lag -= MS_PER_UPDATE;
124 | }
125 |
126 | render();
127 | }
128 | //^6
129 | }
130 | }
131 |
132 |
133 | namespace Interpolate
134 | {
135 | void runGame()
136 | {
137 | const double MS_PER_UPDATE = 8;
138 | double lag = 0;
139 |
140 | //^7
141 | render(lag / MS_PER_UPDATE);
142 | //^7
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/code/cpp/hello-world.h:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | class HelloWorld
4 | {
5 | public:
6 | //^1
7 | // 64 characters --------------------------------------------------------|
8 | static void Do()
9 | {
10 | //^2
11 | std::cout << "Hello world." << std::endl;
12 | //^2
13 | }
14 | //^1
15 | };
--------------------------------------------------------------------------------
/code/cpp/introduction.h:
--------------------------------------------------------------------------------
1 | //
2 | // introduction.h
3 | // cpp
4 | //
5 | // Created by Bob Nystrom on 8/19/13.
6 | // Copyright (c) 2013 Bob Nystrom. All rights reserved.
7 | //
8 |
9 | #ifndef cpp_introduction_h
10 | #define cpp_introduction_h
11 |
12 | //^update
13 | bool update()
14 | {
15 | // Do work...
16 | return isDone();
17 | }
18 | //^update
19 |
20 | #endif
21 |
--------------------------------------------------------------------------------
/code/cpp/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include "hello-world.h"
4 | #include "component.h"
5 | #include "object-pool.h"
6 | #include "service-locator.h"
7 | #include "singleton.h"
8 | #include "double-buffer.h"
9 | #include "subclass-sandbox.h"
10 | #include "type-object.h"
11 | #include "game-loop.h"
12 | #include "spatial-partition.h"
13 | #include "state.h"
14 | #include "update-method.h"
15 | #include "dirty-flag.h"
16 | #include "flyweight.h"
17 | #include "command.h"
18 | #include "observer.h"
19 | #include "prototype.h"
20 | #include "data-locality.h"
21 | #include "event-queue.h"
22 | #include "bytecode.h"
23 |
24 | int main (int argc, char * const argv[])
25 | {
26 | UnbufferedSlapstick::testComedy();
27 | SpatialPartition::test();
28 | ObserverPattern::test();
29 |
30 | return 0;
31 | }
32 |
--------------------------------------------------------------------------------
/code/cpp/object-pool.h:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | namespace Version1
4 | {
5 | class ParticlePool;
6 |
7 | //^1
8 | class Particle
9 | {
10 | public:
11 | Particle()
12 | : framesLeft_(0)
13 | {}
14 |
15 | void init(double x, double y,
16 | double xVel, double yVel, int lifetime)
17 | {
18 | x_ = x; y_ = y;
19 | xVel_ = xVel; yVel_ = yVel;
20 | framesLeft_ = lifetime;
21 | }
22 |
23 | void animate()
24 | {
25 | if (!inUse()) return;
26 |
27 | framesLeft_--;
28 | x_ += xVel_;
29 | y_ += yVel_;
30 | }
31 |
32 | bool inUse() const { return framesLeft_ > 0; }
33 |
34 | private:
35 | int framesLeft_;
36 | double x_, y_;
37 | double xVel_, yVel_;
38 | };
39 | //^1
40 |
41 | //^2
42 | class ParticlePool
43 | {
44 | public:
45 | void create(double x, double y,
46 | double xVel, double yVel, int lifetime);
47 |
48 | void animate()
49 | {
50 | for (int i = 0; i < POOL_SIZE; i++)
51 | {
52 | particles_[i].animate();
53 | }
54 | }
55 |
56 | private:
57 | static const int POOL_SIZE = 100;
58 | Particle particles_[POOL_SIZE];
59 | };
60 | //^2
61 |
62 | //^3
63 | void ParticlePool::create(double x, double y,
64 | double xVel, double yVel,
65 | int lifetime)
66 | {
67 | // Find an available particle.
68 | for (int i = 0; i < POOL_SIZE; i++)
69 | {
70 | if (!particles_[i].inUse())
71 | {
72 | particles_[i].init(x, y, xVel, yVel, lifetime);
73 | //^omit
74 |
75 | std::cout << "created " << i << std::endl;
76 | //^omit
77 | return;
78 | }
79 | }
80 | }
81 | //^3
82 | };
83 |
84 | namespace Temp1
85 | {
86 | //^4
87 | class Particle
88 | {
89 | public:
90 | //^omit
91 | void init(double x, double y,
92 | double xVel, double yVel, int lifetime) {}
93 | bool animate();
94 | bool inUse() { return false; }
95 | double x_;
96 | double y_;
97 | double xVel_;
98 | double yVel_;
99 | //^omit
100 | // ...
101 |
102 | Particle* getNext() const { return state_.next; }
103 | void setNext(Particle* next) { state_.next = next; }
104 |
105 | private:
106 | int framesLeft_;
107 |
108 | union
109 | {
110 | // State when it's in use.
111 | struct
112 | {
113 | double x, y;
114 | double xVel, yVel;
115 | } live;
116 |
117 | // State when it's available.
118 | Particle* next;
119 | } state_;
120 | };
121 | //^4
122 |
123 | //^particle-animate
124 | bool Particle::animate()
125 | {
126 | if (!inUse()) return false;
127 |
128 | framesLeft_--;
129 | x_ += xVel_;
130 | y_ += yVel_;
131 |
132 | return framesLeft_ == 0;
133 | }
134 | //^particle-animate
135 |
136 | //^5
137 | class ParticlePool
138 | {
139 | //^omit
140 | ParticlePool();
141 | void create(double x, double y,
142 | double xVel, double yVel, int lifetime);
143 | void animate();
144 | //^omit
145 | // ...
146 | private:
147 | //^omit
148 | static const int POOL_SIZE = 100;
149 | Particle particles_[POOL_SIZE];
150 | //^omit
151 | Particle* firstAvailable_;
152 | };
153 | //^5
154 |
155 | //^6
156 | ParticlePool::ParticlePool()
157 | {
158 | // The first one is available.
159 | firstAvailable_ = &particles_[0];
160 |
161 | // Each particle points to the next.
162 | for (int i = 0; i < POOL_SIZE - 1; i++)
163 | {
164 | particles_[i].setNext(&particles_[i + 1]);
165 | }
166 |
167 | // The last one terminates the list.
168 | particles_[POOL_SIZE - 1].setNext(NULL);
169 | }
170 | //^6
171 |
172 | //^7
173 | void ParticlePool::create(double x, double y,
174 | double xVel, double yVel,
175 | int lifetime)
176 | {
177 | // Make sure the pool isn't full.
178 | assert(firstAvailable_ != NULL);
179 |
180 | // Remove it from the available list.
181 | Particle* newParticle = firstAvailable_;
182 | firstAvailable_ = newParticle->getNext();
183 |
184 | newParticle->init(x, y, xVel, yVel, lifetime);
185 | }
186 | //^7
187 |
188 | //^8
189 | void ParticlePool::animate()
190 | {
191 | for (int i = 0; i < POOL_SIZE; i++)
192 | {
193 | if (particles_[i].animate())
194 | {
195 | // Add this particle to the front of the list.
196 | particles_[i].setNext(firstAvailable_);
197 | firstAvailable_ = &particles_[i];
198 | }
199 | }
200 | }
201 | //^8
202 | };
203 |
204 | namespace Temp2
205 | {
206 | class ParticlePool;
207 |
208 | //^10
209 | class Particle
210 | {
211 | friend class ParticlePool;
212 |
213 | private:
214 | Particle()
215 | : inUse_(false)
216 | {}
217 |
218 | bool inUse_;
219 | };
220 |
221 | class ParticlePool
222 | {
223 | Particle pool_[100];
224 | };
225 | //^10
226 | }
227 |
228 | namespace Temp3
229 | {
230 | //^11
231 | template
232 | class GenericPool
233 | {
234 | private:
235 | static const int POOL_SIZE = 100;
236 |
237 | TObject pool_[POOL_SIZE];
238 | bool inUse_[POOL_SIZE];
239 | };
240 | //^11
241 | }
242 |
243 | namespace Temp4
244 | {
245 | //^12
246 | class Particle
247 | {
248 | // Multiple ways to initialize.
249 | void init(double x, double y);
250 | void init(double x, double y, double angle);
251 | void init(double x, double y, double xVel, double yVel);
252 | };
253 |
254 | class ParticlePool
255 | {
256 | public:
257 | void create(double x, double y)
258 | {
259 | // Forward to Particle...
260 | }
261 |
262 | void create(double x, double y, double angle)
263 | {
264 | // Forward to Particle...
265 | }
266 |
267 | void create(double x, double y, double xVel, double yVel)
268 | {
269 | // Forward to Particle...
270 | }
271 | };
272 | //^12
273 | }
274 |
275 | namespace Temp5
276 | {
277 | //^13
278 | class Particle
279 | {
280 | public:
281 | // Multiple ways to initialize.
282 | void init(double x, double y);
283 | void init(double x, double y, double angle);
284 | void init(double x, double y, double xVel, double yVel);
285 | };
286 |
287 | class ParticlePool
288 | {
289 | public:
290 | Particle* create()
291 | {
292 | // Return reference to available particle...
293 | //^omit
294 | return &pool_[0];
295 | //^omit
296 | }
297 | private:
298 | Particle pool_[100];
299 | };
300 | //^13
301 |
302 | class Test
303 | {
304 | static void run()
305 | {
306 | //^14
307 | ParticlePool pool;
308 |
309 | pool.create()->init(1, 2);
310 | pool.create()->init(1, 2, 0.3);
311 | pool.create()->init(1, 2, 3.3, 4.4);
312 | //^14
313 |
314 | //^15
315 | Particle* particle = pool.create();
316 | if (particle != NULL) particle->init(1, 2);
317 | //^15
318 | }
319 | };
320 | };
321 |
322 | // 64 characters --------------------------------------------------------|
323 | void TestParticlePool()
324 | {
325 | std::cout << "Object pool" << std::endl;
326 |
327 | Version1::ParticlePool pool = Version1::ParticlePool();
328 |
329 | pool.create(0, 0, 1, 1, 10);
330 | pool.create(1, 0, 1, 1, 10);
331 | pool.create(2, 0, 1, 1, 10);
332 | // pool.update();
333 | }
--------------------------------------------------------------------------------
/code/cpp/prototype.h:
--------------------------------------------------------------------------------
1 | #ifndef cpp_prototype_h
2 | #define cpp_prototype_h
3 |
4 | namespace PrototypePattern
5 | {
6 | namespace Classes
7 | {
8 | //^monster-classes
9 | class Monster
10 | {
11 | // Stuff...
12 | };
13 |
14 | class Ghost : public Monster {};
15 | class Demon : public Monster {};
16 | class Sorcerer : public Monster {};
17 | //^monster-classes
18 |
19 | //^spawner-classes
20 | class Spawner
21 | {
22 | public:
23 | virtual ~Spawner() {}
24 | virtual Monster* spawnMonster() = 0;
25 | };
26 |
27 | class GhostSpawner : public Spawner
28 | {
29 | public:
30 | virtual Monster* spawnMonster()
31 | {
32 | return new Ghost();
33 | }
34 | };
35 |
36 | class DemonSpawner : public Spawner
37 | {
38 | public:
39 | virtual Monster* spawnMonster()
40 | {
41 | return new Demon();
42 | }
43 | };
44 |
45 | // You get the idea...
46 | //^spawner-classes
47 | }
48 |
49 | namespace Clone
50 | {
51 | //^virtual-clone
52 | class Monster
53 | {
54 | public:
55 | virtual ~Monster() {}
56 | virtual Monster* clone() = 0;
57 |
58 | // Other stuff...
59 | };
60 | //^virtual-clone
61 |
62 | //^clone-ghost
63 | class Ghost : public Monster {
64 | public:
65 | Ghost(int health, int speed)
66 | : health_(health),
67 | speed_(speed)
68 | {}
69 |
70 | virtual Monster* clone()
71 | {
72 | return new Ghost(health_, speed_);
73 | }
74 |
75 | private:
76 | int health_;
77 | int speed_;
78 | };
79 | //^clone-ghost
80 |
81 |
82 | //^spawner-clone
83 | class Spawner
84 | {
85 | public:
86 | Spawner(Monster* prototype)
87 | : prototype_(prototype)
88 | {}
89 |
90 | Monster* spawnMonster()
91 | {
92 | return prototype_->clone();
93 | }
94 |
95 | private:
96 | Monster* prototype_;
97 | };
98 | //^spawner-clone
99 |
100 | void test()
101 | {
102 | //^spawn-ghost-clone
103 | Monster* ghostPrototype = new Ghost(15, 3);
104 | Spawner* ghostSpawner = new Spawner(ghostPrototype);
105 | //^spawn-ghost-clone
106 | use(ghostSpawner);
107 | }
108 | }
109 |
110 | namespace Callbacks
111 | {
112 | class Monster
113 | {
114 | // Stuff...
115 | };
116 |
117 | class Ghost : public Monster {};
118 |
119 | //^callback
120 | Monster* spawnGhost()
121 | {
122 | return new Ghost();
123 | }
124 | //^callback
125 |
126 | //^spawner-callback
127 | typedef Monster* (*SpawnCallback)();
128 |
129 | class Spawner
130 | {
131 | public:
132 | Spawner(SpawnCallback spawn)
133 | : spawn_(spawn)
134 | {}
135 |
136 | Monster* spawnMonster()
137 | {
138 | return spawn_();
139 | }
140 |
141 | private:
142 | SpawnCallback spawn_;
143 | };
144 | //^spawner-callback
145 |
146 | void test()
147 | {
148 | //^spawn-ghost-callback
149 | Spawner* ghostSpawner = new Spawner(spawnGhost);
150 | //^spawn-ghost-callback
151 | use(ghostSpawner);
152 | }
153 | }
154 |
155 | namespace Templates
156 | {
157 | class Monster
158 | {
159 | // Stuff...
160 | };
161 |
162 | class Ghost : public Monster {};
163 |
164 | //^templates
165 | class Spawner
166 | {
167 | public:
168 | virtual ~Spawner() {}
169 | virtual Monster* spawnMonster() = 0;
170 | };
171 |
172 | template
173 | class SpawnerFor : public Spawner
174 | {
175 | public:
176 | virtual Monster* spawnMonster() { return new T(); }
177 | };
178 | //^templates
179 |
180 | void test()
181 | {
182 | //^use-templates
183 | Spawner* ghostSpawner = new SpawnerFor();
184 | //^use-templates
185 | use(ghostSpawner);
186 | }
187 | }
188 | }
189 |
190 | #endif
191 |
--------------------------------------------------------------------------------
/code/cpp/service-locator.h:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | class AudioSystem
4 | {
5 | public:
6 | static void playSound(int id) {}
7 | static AudioSystem* instance() { return NULL; }
8 | };
9 |
10 | void example()
11 | {
12 | int VERY_LOUD_BANG = 0;
13 |
14 | //^15
15 | // Use a static class?
16 | AudioSystem::playSound(VERY_LOUD_BANG);
17 |
18 | // Or maybe a singleton?
19 | AudioSystem::instance()->playSound(VERY_LOUD_BANG);
20 | //^15
21 | }
22 |
23 | //^9
24 | class Audio
25 | {
26 | public:
27 | virtual ~Audio() {}
28 | virtual void playSound(int soundID) = 0;
29 | virtual void stopSound(int soundID) = 0;
30 | virtual void stopAllSounds() = 0;
31 | };
32 | //^9
33 |
34 | //^10
35 | class ConsoleAudio : public Audio
36 | {
37 | public:
38 | virtual void playSound(int soundID)
39 | {
40 | // Play sound using console audio api...
41 | }
42 |
43 | virtual void stopSound(int soundID)
44 | {
45 | // Stop sound using console audio api...
46 | }
47 |
48 | virtual void stopAllSounds()
49 | {
50 | // Stop all sounds using console audio api...
51 | }
52 | };
53 | //^10
54 |
55 | //^12
56 | class LoggedAudio : public Audio
57 | {
58 | public:
59 | LoggedAudio(Audio &wrapped)
60 | : wrapped_(wrapped)
61 | {}
62 |
63 | virtual void playSound(int soundID)
64 | {
65 | log("play sound");
66 | wrapped_.playSound(soundID);
67 | }
68 |
69 | virtual void stopSound(int soundID)
70 | {
71 | log("stop sound");
72 | wrapped_.stopSound(soundID);
73 | }
74 |
75 | virtual void stopAllSounds()
76 | {
77 | log("stop all sounds");
78 | wrapped_.stopAllSounds();
79 | }
80 |
81 | private:
82 | void log(const char* message)
83 | {
84 | // Code to log message...
85 | }
86 |
87 | Audio &wrapped_;
88 | };
89 | //^12
90 |
91 | // design decisions / di
92 | namespace Version1
93 | {
94 | //^1
95 | class Locator
96 | {
97 | public:
98 | static Audio* getAudio() { return service_; }
99 |
100 | static void provide(Audio* service)
101 | {
102 | service_ = service;
103 | }
104 |
105 | private:
106 | static Audio* service_;
107 | };
108 | //^1
109 |
110 | Audio *Locator::service_;
111 |
112 | void initGame()
113 | {
114 | //^11
115 | ConsoleAudio *audio = new ConsoleAudio();
116 | Locator::provide(audio);
117 | //^11
118 | }
119 |
120 | void someGameCode()
121 | {
122 | int VERY_LOUD_BANG = 0;
123 | //^5
124 | Audio *audio = Locator::getAudio();
125 | audio->playSound(VERY_LOUD_BANG);
126 | //^5
127 | }
128 | }
129 |
130 | // design decisions / compile time
131 | namespace Version2
132 | {
133 | class DebugAudio: public Audio
134 | {
135 | public:
136 | virtual void playSound(int soundID) { /* Do nothing. */ }
137 | virtual void stopSound(int soundID) { /* Do nothing. */ }
138 | virtual void stopAllSounds() { /* Do nothing. */ }
139 | };
140 | class ReleaseAudio: public DebugAudio {};
141 |
142 | //^2
143 | class Locator
144 | {
145 | public:
146 | static Audio& getAudio() { return service_; }
147 |
148 | private:
149 | #if DEBUG
150 | static DebugAudio service_;
151 | #else
152 | static ReleaseAudio service_;
153 | #endif
154 | };
155 | //^2
156 | }
157 |
158 | // design decisions / scope
159 | namespace Version3
160 | {
161 | //^3
162 | class Base
163 | {
164 | // Code to locate service and set service_...
165 |
166 | protected:
167 | // Derived classes can use service
168 | static Audio& getAudio() { return *service_; }
169 |
170 | private:
171 | static Audio* service_;
172 | };
173 | //^3
174 | }
175 |
176 | namespace Version4
177 | {
178 | //^4
179 | class Locator
180 | {
181 | public:
182 | static Audio& getAudio()
183 | {
184 | Audio* service = NULL;
185 |
186 | // Code here to locate service...
187 |
188 | assert(service != NULL);
189 | return *service;
190 | }
191 | };
192 | //^4
193 | }
194 |
195 | namespace Version5
196 | {
197 | //^7
198 | class NullAudio: public Audio
199 | {
200 | public:
201 | virtual void playSound(int soundID) { /* Do nothing. */ }
202 | virtual void stopSound(int soundID) { /* Do nothing. */ }
203 | virtual void stopAllSounds() { /* Do nothing. */ }
204 | };
205 | //^7
206 |
207 | //^8
208 | class Locator
209 | {
210 | public:
211 | static void initialize() { service_ = &nullService_; }
212 |
213 | static Audio& getAudio() { return *service_; }
214 |
215 | static void provide(Audio* service)
216 | {
217 | if (service == NULL)
218 | {
219 | // Revert to null service.
220 | service_ = &nullService_;
221 | }
222 | else
223 | {
224 | service_ = service;
225 | }
226 | }
227 |
228 | private:
229 | static Audio* service_;
230 | static NullAudio nullService_;
231 | };
232 | //^8
233 |
234 | Audio *Locator::service_ = NULL;
235 | NullAudio Locator::nullService_;
236 |
237 | //^13
238 | void enableAudioLogging()
239 | {
240 | // Decorate the existing service.
241 | Audio *service = new LoggedAudio(Locator::getAudio());
242 |
243 | // Swap it in.
244 | Locator::provide(service);
245 | }
246 | //^13
247 | }
--------------------------------------------------------------------------------
/code/cpp/singleton.h:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | namespace Singleton1
4 | {
5 | //^1
6 | class FileSystem
7 | {
8 | public:
9 | static FileSystem& instance()
10 | {
11 | // Lazy initialize.
12 | if (instance_ == NULL) instance_ = new FileSystem();
13 | return *instance_;
14 | }
15 |
16 | private:
17 | FileSystem() {}
18 |
19 | static FileSystem* instance_;
20 | };
21 | //^1
22 |
23 | FileSystem* FileSystem::instance_;
24 |
25 | void test()
26 | {
27 | if (&FileSystem::instance() == NULL)
28 | {
29 | std::cout << "singleton is null!" << std::endl;
30 | }
31 | else
32 | {
33 | std::cout << "singleton is ok" << std::endl;
34 | }
35 | }
36 | }
37 |
38 | namespace SingletonStatic
39 | {
40 | //^local-static
41 | class FileSystem
42 | {
43 | public:
44 | static FileSystem& instance()
45 | {
46 | static FileSystem *instance = new FileSystem();
47 | return *instance;
48 | }
49 |
50 | private:
51 | FileSystem() {}
52 | };
53 | //^local-static
54 |
55 | void test()
56 | {
57 | if (&FileSystem::instance() == NULL)
58 | {
59 | std::cout << "singleton is null!" << std::endl;
60 | }
61 | else
62 | {
63 | std::cout << "singleton is ok" << std::endl;
64 | }
65 | }
66 | }
67 |
68 | namespace Singleton2
69 | {
70 | //^2
71 | class FileSystem
72 | {
73 | public:
74 | virtual ~FileSystem() {}
75 | virtual char* readFile(char* path) = 0;
76 | virtual void writeFile(char* path, char* contents) = 0;
77 | };
78 | //^2
79 |
80 | //^derived-file-systems
81 | class PS3FileSystem : public FileSystem
82 | {
83 | public:
84 | virtual char* readFile(char* path)
85 | {
86 | // Use Sony file IO API...
87 | //^omit
88 | return NULL;
89 | //^omit
90 | }
91 |
92 | virtual void writeFile(char* path, char* contents)
93 | {
94 | // Use sony file IO API...
95 | }
96 | };
97 |
98 | class WiiFileSystem : public FileSystem
99 | {
100 | public:
101 | virtual char* readFile(char* path)
102 | {
103 | // Use Nintendo file IO API...
104 | //^omit
105 | return NULL;
106 | //^omit
107 | }
108 |
109 | virtual void writeFile(char* path, char* contents)
110 | {
111 | // Use Nintendo file IO API...
112 | }
113 | };
114 | //^derived-file-systems
115 | }
116 |
117 | namespace Singleton3
118 | {
119 | #define PLAYSTATION3 1
120 | #define WII 2
121 | #define PLATFORM PLAYSTATION3
122 |
123 | class PS3FileSystem;
124 | class WiiFileSystem;
125 |
126 | //^3
127 | class FileSystem
128 | {
129 | public:
130 | static FileSystem& instance();
131 |
132 | virtual ~FileSystem() {}
133 | virtual char* readFile(char* path) = 0;
134 | virtual void writeFile(char* path, char* contents) = 0;
135 |
136 | protected:
137 | FileSystem() {}
138 | };
139 | //^3
140 |
141 | class PS3FileSystem : public FileSystem
142 | {
143 | virtual char* readFile(char* path)
144 | {
145 | return NULL;
146 | }
147 |
148 | virtual void writeFile(char* path, char* contents) {}
149 | };
150 |
151 | class WiiFileSystem : public FileSystem
152 | {
153 | virtual char* readFile(char* path)
154 | {
155 | return NULL;
156 | }
157 |
158 | virtual void writeFile(char* path, char* contents) {}
159 | };
160 |
161 | //^4
162 | FileSystem& FileSystem::instance()
163 | {
164 | #if PLATFORM == PLAYSTATION3
165 | static FileSystem *instance = new PS3FileSystem();
166 | #elif PLATFORM == WII
167 | static FileSystem *instance = new WiiFileSystem();
168 | #endif
169 |
170 | return *instance;
171 | }
172 | //^4
173 | }
174 |
175 | namespace Singleton4
176 | {
177 | //^5
178 | class FileSystem
179 | {
180 | public:
181 | static FileSystem& instance() { return instance_; }
182 |
183 | private:
184 | FileSystem() {}
185 |
186 | static FileSystem instance_;
187 | };
188 | //^5
189 |
190 | FileSystem FileSystem::instance_ = FileSystem();
191 | }
192 |
193 | namespace Singleton5
194 | {
195 | //^6
196 | class FileSystem
197 | {
198 | public:
199 | FileSystem()
200 | {
201 | assert(!instantiated_);
202 | instantiated_ = true;
203 | }
204 |
205 | ~FileSystem() { instantiated_ = false; }
206 |
207 | private:
208 | static bool instantiated_;
209 | };
210 |
211 | bool FileSystem::instantiated_ = false;
212 | //^6
213 | }
214 |
215 | namespace Singleton7
216 | {
217 | #define SCREEN_WIDTH 100
218 | #define SCREEN_HEIGHT 100
219 |
220 | //^8
221 | class Bullet
222 | {
223 | public:
224 | int getX() const { return x_; }
225 | int getY() const { return y_; }
226 |
227 | void setX(int x) { x_ = x; }
228 | void setY(int y) { y_ = y; }
229 |
230 | private:
231 | int x_, y_;
232 | };
233 |
234 | class BulletManager
235 | {
236 | public:
237 | Bullet* create(int x, int y)
238 | {
239 | Bullet* bullet = new Bullet();
240 | bullet->setX(x);
241 | bullet->setY(y);
242 |
243 | return bullet;
244 | }
245 |
246 | bool isOnScreen(Bullet& bullet)
247 | {
248 | return bullet.getX() >= 0 &&
249 | bullet.getX() < SCREEN_WIDTH &&
250 | bullet.getY() >= 0 &&
251 | bullet.getY() < SCREEN_HEIGHT;
252 | }
253 |
254 | void move(Bullet& bullet)
255 | {
256 | bullet.setX(bullet.getX() + 5);
257 | }
258 | };
259 | //^8
260 | }
261 |
262 | namespace Singleton8
263 | {
264 | //^9
265 | class Bullet
266 | {
267 | public:
268 | Bullet(int x, int y) : x_(x), y_(y) {}
269 |
270 | bool isOnScreen()
271 | {
272 | return x_ >= 0 && x_ < SCREEN_WIDTH &&
273 | y_ >= 0 && y_ < SCREEN_HEIGHT;
274 | }
275 |
276 | void move() { x_ += 5; }
277 |
278 | private:
279 | int x_, y_;
280 | };
281 | //^9
282 | }
283 |
284 | namespace Singleton9
285 | {
286 | class Log
287 | {
288 | public:
289 | virtual ~Log() {}
290 | virtual void write(const char* text) = 0;
291 | };
292 |
293 | //^10
294 | class GameObject
295 | {
296 | protected:
297 | Log& getLog() { return log_; }
298 |
299 | private:
300 | static Log& log_;
301 | };
302 |
303 | class Enemy : public GameObject
304 | {
305 | void doSomething()
306 | {
307 | getLog().write("I can log!");
308 | }
309 | };
310 | //^10
311 | }
312 |
313 | namespace Singleton10
314 | {
315 | class Log {};
316 | class FileSystem {};
317 | class AudioPlayer
318 | {
319 | public:
320 | virtual void play(int id) = 0;
321 | };
322 |
323 | //^11
324 | class Game
325 | {
326 | public:
327 | static Game& instance() { return instance_; }
328 |
329 | // Functions to set log_, et. al. ...
330 |
331 | Log& getLog() { return *log_; }
332 | FileSystem& getFileSystem() { return *fileSystem_; }
333 | AudioPlayer& getAudioPlayer() { return *audioPlayer_; }
334 |
335 | private:
336 | static Game instance_;
337 |
338 | Log *log_;
339 | FileSystem *fileSystem_;
340 | AudioPlayer *audioPlayer_;
341 | };
342 | //^11
343 |
344 | Game Game::instance_ = Game();
345 |
346 | void foo()
347 | {
348 | int VERY_LOUD_BANG = 0;
349 | //^12
350 | Game::instance().getAudioPlayer().play(VERY_LOUD_BANG);
351 | //^12
352 | }
353 | }
354 |
--------------------------------------------------------------------------------
/code/cpp/subclass-sandbox.h:
--------------------------------------------------------------------------------
1 | typedef int SoundId;
2 | typedef int ParticleType;
3 |
4 | const SoundId SOUND_SPROING = 1;
5 | const SoundId SOUND_SWOOP = 1;
6 | const SoundId SOUND_DIVE = 1;
7 | const ParticleType PARTICLE_DUST = 1;
8 | const ParticleType PARTICLE_SPARKLES = 1;
9 |
10 | namespace SimpleExample
11 | {
12 | //^1
13 | class Superpower
14 | {
15 | public:
16 | virtual ~Superpower() {}
17 |
18 | protected:
19 | virtual void activate() = 0;
20 |
21 | void move(double x, double y, double z)
22 | {
23 | // Code here...
24 | }
25 |
26 | void playSound(SoundId sound, double volume)
27 | {
28 | // Code here...
29 | }
30 |
31 | void spawnParticles(ParticleType type, int count)
32 | {
33 | // Code here...
34 | }
35 | };
36 | //^1
37 |
38 | //^2
39 | class SkyLaunch : public Superpower
40 | {
41 | protected:
42 | virtual void activate()
43 | {
44 | // Spring into the air.
45 | playSound(SOUND_SPROING, 1.0f);
46 | spawnParticles(PARTICLE_DUST, 10);
47 | move(0, 0, 20);
48 | }
49 | };
50 | //^2
51 | }
52 |
53 | namespace Elaborated
54 | {
55 | //^3
56 | class Superpower
57 | {
58 | protected:
59 | //^omit
60 | virtual void activate() = 0;
61 | void move(double x, double y, double z) {}
62 | void playSound(SoundId sound, double volume) {}
63 | void spawnParticles(ParticleType type, int count) {}
64 | //^omit
65 | double getHeroX()
66 | {
67 | // Code here...
68 | //^omit
69 | return 0;
70 | //^omit
71 | }
72 |
73 | double getHeroY()
74 | {
75 | // Code here...
76 | //^omit
77 | return 0;
78 | //^omit
79 | }
80 |
81 | double getHeroZ()
82 | {
83 | // Code here...
84 | //^omit
85 | return 0;
86 | //^omit
87 | }
88 |
89 | // Existing stuff...
90 | };
91 | //^3
92 |
93 | //^4
94 | class SkyLaunch : public Superpower
95 | {
96 | protected:
97 | virtual void activate()
98 | {
99 | if (getHeroZ() == 0)
100 | {
101 | // On the ground, so spring into the air.
102 | playSound(SOUND_SPROING, 1.0f);
103 | spawnParticles(PARTICLE_DUST, 10);
104 | move(0, 0, 20);
105 | }
106 | else if (getHeroZ() < 10.0f)
107 | {
108 | // Near the ground, so do a double jump.
109 | playSound(SOUND_SWOOP, 1.0f);
110 | move(0, 0, getHeroZ() + 20);
111 | }
112 | else
113 | {
114 | // Way up in the air, so do a dive attack.
115 | playSound(SOUND_DIVE, 0.7f);
116 | spawnParticles(PARTICLE_SPARKLES, 1);
117 | move(0, 0, -getHeroZ());
118 | }
119 | }
120 | };
121 | //^4
122 | }
123 |
124 | namespace Forwarding
125 | {
126 | struct SoundEngine
127 | {
128 | void play(SoundId sound, double volume) {}
129 | };
130 |
131 | SoundEngine soundEngine_;
132 |
133 | //^5
134 | void playSound(SoundId sound, double volume)
135 | {
136 | soundEngine_.play(sound, volume);
137 | }
138 | //^5
139 | }
140 |
141 | namespace HelperClassBefore
142 | {
143 | //^6
144 | class Superpower
145 | {
146 | protected:
147 | void playSound(SoundId sound, double volume)
148 | {
149 | // Code here...
150 | }
151 |
152 | void stopSound(SoundId sound)
153 | {
154 | // Code here...
155 | }
156 |
157 | void setVolume(SoundId sound)
158 | {
159 | // Code here...
160 | }
161 |
162 | // Sandbox method and other operations...
163 | };
164 | //^6
165 | };
166 |
167 | namespace HelperClassAfter
168 | {
169 | //^7
170 | class SoundPlayer
171 | {
172 | void playSound(SoundId sound, double volume)
173 | {
174 | // Code here...
175 | }
176 |
177 | void stopSound(SoundId sound)
178 | {
179 | // Code here...
180 | }
181 |
182 | void setVolume(SoundId sound)
183 | {
184 | // Code here...
185 | }
186 | };
187 | //^7
188 |
189 | //^8
190 | class Superpower
191 | {
192 | protected:
193 | SoundPlayer& getSoundPlayer()
194 | {
195 | return soundPlayer_;
196 | }
197 |
198 | // Sandbox method and other operations...
199 |
200 | private:
201 | SoundPlayer soundPlayer_;
202 | };
203 | //^8
204 | }
205 |
206 | namespace PassToConstructor
207 | {
208 | class ParticleSystem {};
209 |
210 | //^pass-to-ctor-base
211 | class Superpower
212 | {
213 | public:
214 | Superpower(ParticleSystem* particles)
215 | : particles_(particles)
216 | {}
217 |
218 | // Sandbox method and other operations...
219 |
220 | private:
221 | ParticleSystem* particles_;
222 | };
223 | //^pass-to-ctor-base
224 |
225 | //^pass-to-ctor-sub
226 | class SkyLaunch : public Superpower
227 | {
228 | public:
229 | SkyLaunch(ParticleSystem* particles)
230 | : Superpower(particles)
231 | {}
232 | };
233 | //^pass-to-ctor-sub
234 | }
235 |
236 | namespace TwoStageInit
237 | {
238 | class ParticleSystem {};
239 |
240 | class Superpower
241 | {
242 | public:
243 | void init(ParticleSystem* particles) {}
244 | };
245 |
246 | ParticleSystem* particles;
247 |
248 | class SkyLaunch : public Superpower {};
249 |
250 | void foo()
251 | {
252 | //^9
253 | Superpower* power = new SkyLaunch();
254 | power->init(particles);
255 | //^9
256 | }
257 | }
258 |
259 | namespace TwoStageInitEncapsulated
260 | {
261 | class ParticleSystem {};
262 |
263 | class Superpower
264 | {
265 | public:
266 | void init(ParticleSystem* audio) {}
267 | };
268 |
269 | class SkyLaunch : public Superpower {};
270 |
271 | //^10
272 | Superpower* createSkyLaunch(ParticleSystem* particles)
273 | {
274 | Superpower* power = new SkyLaunch();
275 | power->init(particles);
276 | return power;
277 | }
278 | //^10
279 | }
280 |
281 | namespace StaticState
282 | {
283 | class ParticleSystem {};
284 |
285 | //^11
286 | class Superpower
287 | {
288 | public:
289 | static void init(ParticleSystem* particles)
290 | {
291 | particles_ = particles;
292 | }
293 |
294 | // Sandbox method and other operations...
295 |
296 | private:
297 | static ParticleSystem* particles_;
298 | };
299 | //^11
300 | }
301 |
302 | namespace UseServiceLocator
303 | {
304 | struct ParticleSystem
305 | {
306 | void spawn(ParticleType type, int count);
307 | };
308 |
309 | ParticleSystem particles;
310 |
311 | class Locator
312 | {
313 | public:
314 | static ParticleSystem& getParticles() { return particles; }
315 | };
316 |
317 | //^12
318 | class Superpower
319 | {
320 | protected:
321 | void spawnParticles(ParticleType type, int count)
322 | {
323 | ParticleSystem& particles = Locator::getParticles();
324 | particles.spawn(type, count);
325 | }
326 |
327 | // Sandbox method and other operations...
328 | };
329 | //^12
330 | }
331 |
--------------------------------------------------------------------------------
/code/cpp/type-object.h:
--------------------------------------------------------------------------------
1 | #include "common.h"
2 |
3 | namespace Subclasses
4 | {
5 | //^1
6 | class Monster
7 | {
8 | public:
9 | virtual ~Monster() {}
10 | virtual const char* getAttack() = 0;
11 |
12 | protected:
13 | Monster(int startingHealth)
14 | : health_(startingHealth)
15 | {}
16 |
17 | private:
18 | int health_; // Current health.
19 | };
20 | //^1
21 |
22 | //^2
23 | class Dragon : public Monster
24 | {
25 | public:
26 | Dragon() : Monster(230) {}
27 |
28 | virtual const char* getAttack()
29 | {
30 | return "The dragon breathes fire!";
31 | }
32 | };
33 |
34 | class Troll : public Monster
35 | {
36 | public:
37 | Troll() : Monster(48) {}
38 |
39 | virtual const char* getAttack()
40 | {
41 | return "The troll clubs you!";
42 | }
43 | };
44 | //^2
45 | }
46 |
47 | namespace NoInheritance
48 | {
49 | //^3
50 | class Breed
51 | {
52 | public:
53 | Breed(int health, const char* attack)
54 | : health_(health),
55 | attack_(attack)
56 | {}
57 |
58 | int getHealth() { return health_; }
59 | const char* getAttack() { return attack_; }
60 |
61 | private:
62 | int health_; // Starting health.
63 | const char* attack_;
64 | };
65 | //^3
66 |
67 | //^4
68 | class Monster
69 | {
70 | public:
71 | Monster(Breed& breed)
72 | : health_(breed.getHealth()),
73 | breed_(breed)
74 | {}
75 |
76 | const char* getAttack()
77 | {
78 | return breed_.getAttack();
79 | }
80 |
81 | private:
82 | int health_; // Current health.
83 | Breed& breed_;
84 | };
85 | //^4
86 |
87 | void testUsage()
88 | {
89 | Breed& someBreed = *(new Breed(123, "attack"));
90 |
91 | //^7
92 | Monster* monster = new Monster(someBreed);
93 | //^7
94 | use(monster);
95 | }
96 | }
97 |
98 | namespace BreedCtor
99 | {
100 | class Breed;
101 |
102 | class Monster
103 | {
104 | public:
105 | Monster(Breed& breed) {}
106 | };
107 |
108 | //^5
109 | class Breed
110 | {
111 | public:
112 | Monster* newMonster() { return new Monster(*this); }
113 |
114 | // Previous Breed code...
115 | //^omit
116 | Breed(int health, const char* attack)
117 | : health_(health),
118 | attack_(attack)
119 | {}
120 |
121 | int getHealth() { return health_; }
122 | const char* getAttack() { return attack_; }
123 |
124 | private:
125 | int health_; // Starting health.
126 | const char* attack_;
127 | //^omit
128 | };
129 | //^5
130 |
131 | void testUsage()
132 | {
133 | Breed& someBreed = *(new Breed(1, "foo"));
134 |
135 | //^8
136 | Monster* monster = someBreed.newMonster();
137 | //^8
138 | use(monster);
139 | }
140 | }
141 |
142 | namespace BreedCtorMonster
143 | {
144 | class Breed
145 | {
146 | public:
147 | int getHealth() { return 0; }
148 | const char* getAttack() { return "s"; }
149 | };
150 |
151 | //^6
152 | class Monster
153 | {
154 | friend class Breed;
155 |
156 | public:
157 | const char* getAttack() { return breed_.getAttack(); }
158 |
159 | private:
160 | Monster(Breed& breed)
161 | : health_(breed.getHealth()),
162 | breed_(breed)
163 | {}
164 |
165 | int health_; // Current health.
166 | Breed& breed_;
167 | };
168 | //^6
169 | }
170 |
171 | namespace Inheritance
172 | {
173 | //^9
174 | class Breed
175 | {
176 | public:
177 | Breed(Breed* parent, int health, const char* attack)
178 | : parent_(parent),
179 | health_(health),
180 | attack_(attack)
181 | {}
182 |
183 | int getHealth();
184 | const char* getAttack();
185 |
186 | private:
187 | Breed* parent_;
188 | int health_; // Starting health.
189 | const char* attack_;
190 | };
191 | //^9
192 |
193 | //^10
194 | int Breed::getHealth()
195 | {
196 | // Override.
197 | if (health_ != 0 || parent_ == NULL) return health_;
198 |
199 | // Inherit.
200 | return parent_->getHealth();
201 | }
202 |
203 | const char* Breed::getAttack()
204 | {
205 | // Override.
206 | if (attack_ != NULL || parent_ == NULL) return attack_;
207 |
208 | // Inherit.
209 | return parent_->getAttack();
210 | }
211 | //^10
212 | }
213 |
214 | namespace CopyDown
215 | {
216 | class Breed
217 | {
218 | public:
219 | //^copy-down
220 | Breed(Breed* parent, int health, const char* attack)
221 | : health_(health),
222 | attack_(attack)
223 | {
224 | // Inherit non-overridden attributes.
225 | if (parent != NULL)
226 | {
227 | if (health == 0) health_ = parent->getHealth();
228 | if (attack == NULL) attack_ = parent->getAttack();
229 | }
230 | }
231 | //^copy-down
232 |
233 | //^copy-down-access
234 | int getHealth() { return health_; }
235 | const char* getAttack() { return attack_; }
236 | //^copy-down-access
237 |
238 | private:
239 | int health_; // Starting health.
240 | const char* attack_;
241 | };
242 | }
243 |
244 | namespace ExposeBreed
245 | {
246 | class Breed;
247 |
248 | //^11
249 | class Monster
250 | {
251 | public:
252 | Breed& getBreed() { return breed_; }
253 |
254 | // Existing code...
255 | //^omit
256 | Breed& breed_;
257 | //^omit
258 | };
259 | //^11
260 | }
261 |
262 | namespace OverrideAttack
263 | {
264 | class Breed
265 | {
266 | public:
267 | Breed(const char* attack)
268 | : attack_(attack)
269 | {}
270 |
271 | const char* getAttack() { return attack_; }
272 |
273 | private:
274 | const char* attack_;
275 | };
276 |
277 | class Monster
278 | {
279 | public:
280 | Monster(Breed& breed)
281 | : breed_(breed)
282 | {}
283 |
284 | const char* getAttack();
285 |
286 | private:
287 | int health_;
288 | Breed& breed_;
289 | };
290 |
291 | #define LOW_HEALTH 1
292 |
293 | //^12
294 | const char* Monster::getAttack()
295 | {
296 | if (health_ < LOW_HEALTH)
297 | {
298 | return "The monster flails weakly.";
299 | }
300 |
301 | return breed_.getAttack();
302 | }
303 | //^12
304 | }
--------------------------------------------------------------------------------
/code/cpp/update-method.h:
--------------------------------------------------------------------------------
1 | //
2 | // update-method.h
3 | // cpp
4 | //
5 | // Created by Bob Nystrom on 8/6/13.
6 | // Copyright (c) 2013 Bob Nystrom. All rights reserved.
7 | //
8 |
9 | #ifndef cpp_update_method_h
10 | #define cpp_update_method_h
11 |
12 | namespace UpdateMethod
13 | {
14 | namespace Motivation
15 | {
16 | struct Entity
17 | {
18 | void shootLightning() {}
19 | void setX(double x) {}
20 | };
21 |
22 | void refreshGame() {}
23 |
24 | void justPatrol()
25 | {
26 | Entity skeleton;
27 |
28 | //^just-patrol
29 | while (true)
30 | {
31 | // Patrol right.
32 | for (double x = 0; x < 100; x++)
33 | {
34 | skeleton.setX(x);
35 | }
36 |
37 | // Patrol left.
38 | for (double x = 100; x > 0; x--)
39 | {
40 | skeleton.setX(x);
41 | }
42 | }
43 | //^just-patrol
44 | }
45 |
46 | void patrolInLoop()
47 | {
48 | //^patrol-in-loop
49 | Entity skeleton;
50 | bool patrollingLeft = false;
51 | double x = 0;
52 |
53 | // Main game loop:
54 | while (true)
55 | {
56 | if (patrollingLeft)
57 | {
58 | x--;
59 | if (x == 0) patrollingLeft = false;
60 | }
61 | else
62 | {
63 | x++;
64 | if (x == 100) patrollingLeft = true;
65 | }
66 |
67 | skeleton.setX(x);
68 |
69 | // Handle user input and render game...
70 | }
71 | //^patrol-in-loop
72 | }
73 |
74 | void statues()
75 | {
76 | //^statues
77 | // Skeleton variables...
78 | Entity leftStatue;
79 | Entity rightStatue;
80 | int leftStatueFrames = 0;
81 | int rightStatueFrames = 0;
82 |
83 | // Main game loop:
84 | while (true)
85 | {
86 | // Skeleton code...
87 |
88 | if (++leftStatueFrames == 90)
89 | {
90 | leftStatueFrames = 0;
91 | leftStatue.shootLightning();
92 | }
93 |
94 | if (++rightStatueFrames == 80)
95 | {
96 | rightStatueFrames = 0;
97 | rightStatue.shootLightning();
98 | }
99 |
100 | // Handle user input and render game...
101 | }
102 | //^statues
103 | }
104 | }
105 |
106 | namespace KeepInMind
107 | {
108 | struct Entity
109 | {
110 | void setPosition(int x, int y) {}
111 | void shootLightning() {}
112 | void update() {}
113 | };
114 |
115 | static const int MAX_ENTITIES = 10;
116 |
117 | void refreshGame() {}
118 |
119 | void skipAdded()
120 | {
121 | int numObjects_ = 0;
122 | Entity* objects_[MAX_ENTITIES];
123 | //^skip-added
124 | int numObjectsThisTurn = numObjects_;
125 | for (int i = 0; i < numObjectsThisTurn; i++)
126 | {
127 | objects_[i]->update();
128 | }
129 | //^skip-added
130 | }
131 |
132 | void skipRemoved()
133 | {
134 | int numObjects_ = 0;
135 | Entity* objects_[MAX_ENTITIES];
136 |
137 | //^skip-removed
138 | for (int i = 0; i < numObjects_; i++)
139 | {
140 | objects_[i]->update();
141 | }
142 | //^skip-removed
143 | }
144 | }
145 |
146 | namespace SampleCode
147 | {
148 | static const int MAX_ENTITIES = 10;
149 |
150 | //^entity-class
151 | class Entity
152 | {
153 | public:
154 | Entity()
155 | : x_(0), y_(0)
156 | {}
157 |
158 | virtual ~Entity() {}
159 | virtual void update() = 0;
160 |
161 | double x() const { return x_; }
162 | double y() const { return y_; }
163 |
164 | void setX(double x) { x_ = x; }
165 | void setY(double y) { y_ = y; }
166 |
167 | private:
168 | double x_;
169 | double y_;
170 | };
171 | //^entity-class
172 |
173 | //^game-world
174 | class World
175 | {
176 | public:
177 | World()
178 | : numEntities_(0)
179 | {}
180 |
181 | void gameLoop();
182 |
183 | private:
184 | Entity* entities_[MAX_ENTITIES];
185 | int numEntities_;
186 | };
187 | //^game-world
188 |
189 | //^game-loop
190 | void World::gameLoop()
191 | {
192 | while (true)
193 | {
194 | // Handle user input...
195 |
196 | // Update each entity.
197 | //^update-component-entities
198 | for (int i = 0; i < numEntities_; i++)
199 | {
200 | entities_[i]->update();
201 | }
202 | //^update-component-entities
203 |
204 | // Physics and rendering...
205 | }
206 | }
207 | //^game-loop
208 |
209 | //^skeleton
210 | class Skeleton : public Entity
211 | {
212 | public:
213 | Skeleton()
214 | : patrollingLeft_(false)
215 | {}
216 |
217 | virtual void update()
218 | {
219 | if (patrollingLeft_)
220 | {
221 | setX(x() - 1);
222 | if (x() == 0) patrollingLeft_ = false;
223 | }
224 | else
225 | {
226 | setX(x() + 1);
227 | if (x() == 100) patrollingLeft_ = true;
228 | }
229 | }
230 |
231 | private:
232 | bool patrollingLeft_;
233 | };
234 | //^skeleton
235 |
236 | //^statue
237 | class Statue : public Entity
238 | {
239 | public:
240 | Statue(int delay)
241 | : frames_(0),
242 | delay_(delay)
243 | {}
244 |
245 | virtual void update()
246 | {
247 | if (++frames_ == delay_)
248 | {
249 | shootLightning();
250 |
251 | // Reset the timer.
252 | frames_ = 0;
253 | }
254 | }
255 |
256 | private:
257 | int frames_;
258 | int delay_;
259 |
260 | void shootLightning()
261 | {
262 | // Shoot the lightning...
263 | }
264 | };
265 | //^statue
266 | }
267 |
268 | namespace ForwardToDelegate
269 | {
270 | class Entity;
271 |
272 | class Entity
273 | {
274 | public:
275 | Entity* state_;
276 | void update();
277 | };
278 |
279 | //^forward
280 | void Entity::update()
281 | {
282 | // Forward to state object.
283 | state_->update();
284 | }
285 | //^forward
286 | }
287 |
288 | namespace VariableTimeStep
289 | {
290 | class Skeleton
291 | {
292 | public:
293 | double x;
294 | bool patrollingLeft_;
295 | void update(double elapsed);
296 | };
297 |
298 | //^variable
299 | void Skeleton::update(double elapsed)
300 | {
301 | if (patrollingLeft_)
302 | {
303 | x -= elapsed;
304 | if (x <= 0)
305 | {
306 | patrollingLeft_ = false;
307 | x = -x;
308 | }
309 | }
310 | else
311 | {
312 | x += elapsed;
313 | if (x >= 100)
314 | {
315 | patrollingLeft_ = true;
316 | x = 100 - (x - 100);
317 | }
318 | }
319 | }
320 | //^variable
321 | }
322 |
323 | }
324 |
325 | #endif
326 |
--------------------------------------------------------------------------------
/code/dart/spatial-partition/pigeonhole_comparisons.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math';
2 |
3 | const MAX_POS = 100;
4 |
5 | const NUM_UNITS = 200;
6 | const NUM_RUNS = 100;
7 |
8 | final rand = new Random();
9 |
10 | class Unit {
11 | final int pos;
12 | Unit(this.pos);
13 | }
14 |
15 | List makeUnits(int seed) {
16 | var rand = new Random(seed);
17 |
18 | var units = [];
19 | for (var i = 0; i < NUM_UNITS; i++) {
20 | units.add(new Unit(rand.nextInt(MAX_POS)));
21 | }
22 |
23 | return units;
24 | }
25 |
26 | slow() {
27 | var compares = 0;
28 | var hits = 0;
29 |
30 | for (var i = 0; i < NUM_RUNS; i++) {
31 | var units = makeUnits(i);
32 |
33 | for (var a = 0; a < units.length - 1; a++) {
34 | for (var b = a + 1; b < units.length; b++) {
35 | compares++;
36 | if (units[a].pos == units[b].pos) {
37 | hits++;
38 | }
39 | }
40 | }
41 | }
42 |
43 | compares /= NUM_RUNS;
44 | hits /= NUM_RUNS;
45 | print("slow: $compares compares, $hits hits");
46 | }
47 |
48 | fast() {
49 | var compares = 0;
50 | var hits = 0;
51 |
52 | for (var i = 0; i < NUM_RUNS; i++) {
53 | var units = makeUnits(i);
54 |
55 | var slots = new List.generate(MAX_POS, (_) => []);
56 | for (var unit in units) slots[unit.pos].add(unit);
57 |
58 | for (var slot in slots) {
59 | for (var a = 0; a < slot.length - 1; a++) {
60 | for (var b = a + 1; b < slot.length; b++) {
61 | compares++;
62 | hits++;
63 | }
64 | }
65 | }
66 | }
67 |
68 | compares /= NUM_RUNS;
69 | hits /= NUM_RUNS;
70 | print("fast: $compares compares, $hits hits");
71 | }
72 |
73 | main() {
74 | slow();
75 | fast();
76 | }
--------------------------------------------------------------------------------
/code/flyweight/tiles/tiles.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 2985588017E5645E00BA78C0 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2985587F17E5645E00BA78C0 /* main.cpp */; };
11 | 2985588217E5645E00BA78C0 /* tiles.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 2985588117E5645E00BA78C0 /* tiles.1 */; };
12 | /* End PBXBuildFile section */
13 |
14 | /* Begin PBXCopyFilesBuildPhase section */
15 | 2985587A17E5645E00BA78C0 /* CopyFiles */ = {
16 | isa = PBXCopyFilesBuildPhase;
17 | buildActionMask = 2147483647;
18 | dstPath = /usr/share/man/man1/;
19 | dstSubfolderSpec = 0;
20 | files = (
21 | 2985588217E5645E00BA78C0 /* tiles.1 in CopyFiles */,
22 | );
23 | runOnlyForDeploymentPostprocessing = 1;
24 | };
25 | /* End PBXCopyFilesBuildPhase section */
26 |
27 | /* Begin PBXFileReference section */
28 | 2985587C17E5645E00BA78C0 /* tiles */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = tiles; sourceTree = BUILT_PRODUCTS_DIR; };
29 | 2985587F17E5645E00BA78C0 /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; };
30 | 2985588117E5645E00BA78C0 /* tiles.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = tiles.1; sourceTree = ""; };
31 | /* End PBXFileReference section */
32 |
33 | /* Begin PBXFrameworksBuildPhase section */
34 | 2985587917E5645E00BA78C0 /* Frameworks */ = {
35 | isa = PBXFrameworksBuildPhase;
36 | buildActionMask = 2147483647;
37 | files = (
38 | );
39 | runOnlyForDeploymentPostprocessing = 0;
40 | };
41 | /* End PBXFrameworksBuildPhase section */
42 |
43 | /* Begin PBXGroup section */
44 | 2985587317E5645E00BA78C0 = {
45 | isa = PBXGroup;
46 | children = (
47 | 2985587E17E5645E00BA78C0 /* tiles */,
48 | 2985587D17E5645E00BA78C0 /* Products */,
49 | );
50 | sourceTree = "";
51 | };
52 | 2985587D17E5645E00BA78C0 /* Products */ = {
53 | isa = PBXGroup;
54 | children = (
55 | 2985587C17E5645E00BA78C0 /* tiles */,
56 | );
57 | name = Products;
58 | sourceTree = "";
59 | };
60 | 2985587E17E5645E00BA78C0 /* tiles */ = {
61 | isa = PBXGroup;
62 | children = (
63 | 2985587F17E5645E00BA78C0 /* main.cpp */,
64 | 2985588117E5645E00BA78C0 /* tiles.1 */,
65 | );
66 | path = tiles;
67 | sourceTree = "";
68 | };
69 | /* End PBXGroup section */
70 |
71 | /* Begin PBXNativeTarget section */
72 | 2985587B17E5645E00BA78C0 /* tiles */ = {
73 | isa = PBXNativeTarget;
74 | buildConfigurationList = 2985588517E5645E00BA78C0 /* Build configuration list for PBXNativeTarget "tiles" */;
75 | buildPhases = (
76 | 2985587817E5645E00BA78C0 /* Sources */,
77 | 2985587917E5645E00BA78C0 /* Frameworks */,
78 | 2985587A17E5645E00BA78C0 /* CopyFiles */,
79 | );
80 | buildRules = (
81 | );
82 | dependencies = (
83 | );
84 | name = tiles;
85 | productName = tiles;
86 | productReference = 2985587C17E5645E00BA78C0 /* tiles */;
87 | productType = "com.apple.product-type.tool";
88 | };
89 | /* End PBXNativeTarget section */
90 |
91 | /* Begin PBXProject section */
92 | 2985587417E5645E00BA78C0 /* Project object */ = {
93 | isa = PBXProject;
94 | attributes = {
95 | LastUpgradeCheck = 0460;
96 | ORGANIZATIONNAME = "Bob Nystrom";
97 | };
98 | buildConfigurationList = 2985587717E5645E00BA78C0 /* Build configuration list for PBXProject "tiles" */;
99 | compatibilityVersion = "Xcode 3.2";
100 | developmentRegion = English;
101 | hasScannedForEncodings = 0;
102 | knownRegions = (
103 | en,
104 | );
105 | mainGroup = 2985587317E5645E00BA78C0;
106 | productRefGroup = 2985587D17E5645E00BA78C0 /* Products */;
107 | projectDirPath = "";
108 | projectRoot = "";
109 | targets = (
110 | 2985587B17E5645E00BA78C0 /* tiles */,
111 | );
112 | };
113 | /* End PBXProject section */
114 |
115 | /* Begin PBXSourcesBuildPhase section */
116 | 2985587817E5645E00BA78C0 /* Sources */ = {
117 | isa = PBXSourcesBuildPhase;
118 | buildActionMask = 2147483647;
119 | files = (
120 | 2985588017E5645E00BA78C0 /* main.cpp in Sources */,
121 | );
122 | runOnlyForDeploymentPostprocessing = 0;
123 | };
124 | /* End PBXSourcesBuildPhase section */
125 |
126 | /* Begin XCBuildConfiguration section */
127 | 2985588317E5645E00BA78C0 /* Debug */ = {
128 | isa = XCBuildConfiguration;
129 | buildSettings = {
130 | ALWAYS_SEARCH_USER_PATHS = NO;
131 | ARCHS = "$(ARCHS_STANDARD_64_BIT)";
132 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
133 | CLANG_CXX_LIBRARY = "libc++";
134 | CLANG_WARN_CONSTANT_CONVERSION = YES;
135 | CLANG_WARN_EMPTY_BODY = YES;
136 | CLANG_WARN_ENUM_CONVERSION = YES;
137 | CLANG_WARN_INT_CONVERSION = YES;
138 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
139 | COPY_PHASE_STRIP = NO;
140 | GCC_C_LANGUAGE_STANDARD = gnu99;
141 | GCC_DYNAMIC_NO_PIC = NO;
142 | GCC_ENABLE_OBJC_EXCEPTIONS = YES;
143 | GCC_OPTIMIZATION_LEVEL = 0;
144 | GCC_PREPROCESSOR_DEFINITIONS = (
145 | "DEBUG=1",
146 | "$(inherited)",
147 | );
148 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
149 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
150 | GCC_WARN_ABOUT_RETURN_TYPE = YES;
151 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
152 | GCC_WARN_UNUSED_VARIABLE = YES;
153 | MACOSX_DEPLOYMENT_TARGET = 10.8;
154 | ONLY_ACTIVE_ARCH = YES;
155 | SDKROOT = macosx;
156 | };
157 | name = Debug;
158 | };
159 | 2985588417E5645E00BA78C0 /* Release */ = {
160 | isa = XCBuildConfiguration;
161 | buildSettings = {
162 | ALWAYS_SEARCH_USER_PATHS = NO;
163 | ARCHS = "$(ARCHS_STANDARD_64_BIT)";
164 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
165 | CLANG_CXX_LIBRARY = "libc++";
166 | CLANG_WARN_CONSTANT_CONVERSION = YES;
167 | CLANG_WARN_EMPTY_BODY = YES;
168 | CLANG_WARN_ENUM_CONVERSION = YES;
169 | CLANG_WARN_INT_CONVERSION = YES;
170 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
171 | COPY_PHASE_STRIP = YES;
172 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
173 | GCC_C_LANGUAGE_STANDARD = gnu99;
174 | GCC_ENABLE_OBJC_EXCEPTIONS = YES;
175 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
176 | GCC_WARN_ABOUT_RETURN_TYPE = YES;
177 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
178 | GCC_WARN_UNUSED_VARIABLE = YES;
179 | MACOSX_DEPLOYMENT_TARGET = 10.8;
180 | SDKROOT = macosx;
181 | };
182 | name = Release;
183 | };
184 | 2985588617E5645E00BA78C0 /* Debug */ = {
185 | isa = XCBuildConfiguration;
186 | buildSettings = {
187 | GCC_ENABLE_CPP_EXCEPTIONS = NO;
188 | GCC_ENABLE_CPP_RTTI = NO;
189 | PRODUCT_NAME = "$(TARGET_NAME)";
190 | };
191 | name = Debug;
192 | };
193 | 2985588717E5645E00BA78C0 /* Release */ = {
194 | isa = XCBuildConfiguration;
195 | buildSettings = {
196 | GCC_ENABLE_CPP_EXCEPTIONS = NO;
197 | GCC_ENABLE_CPP_RTTI = NO;
198 | PRODUCT_NAME = "$(TARGET_NAME)";
199 | };
200 | name = Release;
201 | };
202 | /* End XCBuildConfiguration section */
203 |
204 | /* Begin XCConfigurationList section */
205 | 2985587717E5645E00BA78C0 /* Build configuration list for PBXProject "tiles" */ = {
206 | isa = XCConfigurationList;
207 | buildConfigurations = (
208 | 2985588317E5645E00BA78C0 /* Debug */,
209 | 2985588417E5645E00BA78C0 /* Release */,
210 | );
211 | defaultConfigurationIsVisible = 0;
212 | defaultConfigurationName = Release;
213 | };
214 | 2985588517E5645E00BA78C0 /* Build configuration list for PBXNativeTarget "tiles" */ = {
215 | isa = XCConfigurationList;
216 | buildConfigurations = (
217 | 2985588617E5645E00BA78C0 /* Debug */,
218 | 2985588717E5645E00BA78C0 /* Release */,
219 | );
220 | defaultConfigurationIsVisible = 0;
221 | };
222 | /* End XCConfigurationList section */
223 | };
224 | rootObject = 2985587417E5645E00BA78C0 /* Project object */;
225 | }
226 |
--------------------------------------------------------------------------------
/code/flyweight/tiles/tiles/main.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // main.cpp
3 | // tiles
4 | //
5 | // Created by Bob Nystrom on 9/14/13.
6 | // Copyright (c) 2013 Bob Nystrom. All rights reserved.
7 | //
8 |
9 | #include
10 | #include
11 |
12 | #define ADD_OTHER_STUFF
13 |
14 | static const int SIZE = 128;
15 |
16 | struct timeval startTime;
17 |
18 | void startProfile()
19 | {
20 | gettimeofday(&startTime, NULL);
21 | }
22 |
23 | void endProfile(const char* message)
24 | {
25 | struct timeval endTime;
26 | gettimeofday(&endTime, NULL);
27 |
28 | endTime.tv_sec -= startTime.tv_sec;
29 | endTime.tv_usec -= startTime.tv_usec;
30 |
31 | printf("%s: %lds %dμs\n", message, endTime.tv_sec, endTime.tv_usec);
32 | }
33 |
34 | int pickTile(int x, int y) {
35 | return (x * 7 + y * 13) % 4;
36 | }
37 |
38 | namespace tileEnum
39 | {
40 | enum Tile
41 | {
42 | TILE_GRASS = 0,
43 | TILE_FOREST,
44 | TILE_MOUNTAIN,
45 | TILE_WATER
46 | };
47 |
48 | struct World
49 | {
50 | Tile tiles[SIZE * SIZE];
51 | World()
52 | {
53 | for (int y = 0; y < SIZE; y++)
54 | {
55 | for (int x = 0; x < SIZE; x++)
56 | {
57 | tiles[y * SIZE + x] = (Tile)pickTile(x, y);
58 | }
59 | }
60 | }
61 |
62 | int addTiles()
63 | {
64 | int sum = 0;
65 | for (int y = 0; y < SIZE; y++)
66 | {
67 | for (int x = 0; x < SIZE; x++)
68 | {
69 | switch (tiles[y * SIZE + x])
70 | {
71 | case TILE_GRASS: sum += 1; break;
72 | case TILE_FOREST: sum += 2; break;
73 | case TILE_MOUNTAIN: sum += 3; break;
74 | case TILE_WATER: sum += 4; break;
75 | }
76 | }
77 | }
78 | return sum;
79 | }
80 | };
81 | };
82 |
83 | namespace tileByte
84 | {
85 | enum Tile
86 | {
87 | TILE_GRASS = 0,
88 | TILE_FOREST,
89 | TILE_MOUNTAIN,
90 | TILE_WATER
91 | };
92 |
93 | struct World
94 | {
95 | unsigned char tiles[SIZE * SIZE];
96 | World()
97 | {
98 | for (int y = 0; y < SIZE; y++)
99 | {
100 | for (int x = 0; x < SIZE; x++)
101 | {
102 | tiles[y * SIZE + x] = pickTile(x, y);
103 | }
104 | }
105 | }
106 |
107 | int addTiles()
108 | {
109 | int sum = 0;
110 | for (int y = 0; y < SIZE; y++)
111 | {
112 | for (int x = 0; x < SIZE; x++)
113 | {
114 | switch (tiles[y * SIZE + x])
115 | {
116 | case TILE_GRASS: sum += 1; break;
117 | case TILE_FOREST: sum += 2; break;
118 | case TILE_MOUNTAIN: sum += 3; break;
119 | case TILE_WATER: sum += 4; break;
120 | }
121 | }
122 | }
123 | return sum;
124 | }
125 | };
126 | };
127 |
128 | namespace tileField
129 | {
130 | struct Tile
131 | {
132 | int weight;
133 | #ifdef ADD_OTHER_STUFF
134 | int other[16];
135 | #endif
136 | };
137 |
138 | struct World
139 | {
140 | Tile tileTypes[4];
141 |
142 | Tile* tiles[SIZE * SIZE];
143 | World()
144 | {
145 | tileTypes[0].weight = 1;
146 | tileTypes[1].weight = 2;
147 | tileTypes[2].weight = 3;
148 | tileTypes[3].weight = 4;
149 |
150 | for (int y = 0; y < SIZE; y++)
151 | {
152 | for (int x = 0; x < SIZE; x++)
153 | {
154 | tiles[y * SIZE + x] = &tileTypes[pickTile(x, y)];
155 | }
156 | }
157 | }
158 |
159 | int addTiles()
160 | {
161 | int sum = 0;
162 | for (int y = 0; y < SIZE; y++)
163 | {
164 | for (int x = 0; x < SIZE; x++)
165 | {
166 | sum += tiles[y * SIZE + x]->weight;
167 | }
168 | }
169 | return sum;
170 | }
171 | };
172 | };
173 |
174 | namespace tileVirtual
175 | {
176 | struct Tile
177 | {
178 | virtual int weight() = 0;
179 | };
180 |
181 | struct GrassTile : public Tile
182 | {
183 | virtual int weight() { return 1; }
184 | };
185 |
186 | struct ForestTile : public Tile
187 | {
188 | virtual int weight() { return 2; }
189 | };
190 |
191 | struct MountainTile : public Tile
192 | {
193 | virtual int weight() { return 3; }
194 | };
195 |
196 | struct WaterTile : public Tile
197 | {
198 | virtual int weight() { return 4; }
199 | };
200 |
201 | struct World
202 | {
203 | GrassTile grass;
204 | ForestTile forest;
205 | MountainTile mountain;
206 | WaterTile water;
207 | Tile* tileTypes[4];
208 |
209 | Tile* tiles[SIZE * SIZE];
210 | World()
211 | {
212 | tileTypes[0] = &grass;
213 | tileTypes[1] = &forest;
214 | tileTypes[2] = &mountain;
215 | tileTypes[3] = &water;
216 |
217 | for (int y = 0; y < SIZE; y++)
218 | {
219 | for (int x = 0; x < SIZE; x++)
220 | {
221 | tiles[y * SIZE + x] = tileTypes[pickTile(x, y)];
222 | }
223 | }
224 | }
225 |
226 | int addTiles()
227 | {
228 | int sum = 0;
229 | for (int y = 0; y < SIZE; y++)
230 | {
231 | for (int x = 0; x < SIZE; x++)
232 | {
233 | sum += tiles[y * SIZE + x]->weight();
234 | }
235 | }
236 | return sum;
237 | }
238 | };
239 | };
240 |
241 | int main(int argc, const char * argv[])
242 | {
243 | for (int i = 0; i < 1; i++)
244 | {
245 | {
246 | tileEnum::World world;
247 | startProfile();
248 | int sum = world.addTiles();
249 | endProfile("tile enum");
250 | printf("%d\n", sum);
251 | }
252 |
253 | {
254 | tileByte::World world;
255 | startProfile();
256 | int sum = world.addTiles();
257 | endProfile("tile byte");
258 | printf("%d\n", sum);
259 | }
260 |
261 | {
262 | tileField::World world;
263 | startProfile();
264 | int sum = world.addTiles();
265 | endProfile("tile field");
266 | printf("%d\n", sum);
267 | }
268 |
269 | {
270 | tileVirtual::World world;
271 | startProfile();
272 | int sum = world.addTiles();
273 | endProfile("tile virtual");
274 | printf("%d\n", sum);
275 | }
276 | }
277 |
278 | return 0;
279 | }
280 |
--------------------------------------------------------------------------------
/code/flyweight/tiles/tiles/tiles.1:
--------------------------------------------------------------------------------
1 | .\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples.
2 | .\"See Also:
3 | .\"man mdoc.samples for a complete listing of options
4 | .\"man mdoc for the short list of editing options
5 | .\"/usr/share/misc/mdoc.template
6 | .Dd 9/14/13 \" DATE
7 | .Dt tiles 1 \" Program name and manual section number
8 | .Os Darwin
9 | .Sh NAME \" Section Header - required - don't modify
10 | .Nm tiles,
11 | .\" The following lines are read in generating the apropos(man -k) database. Use only key
12 | .\" words here as the database is built based on the words here and in the .ND line.
13 | .Nm Other_name_for_same_program(),
14 | .Nm Yet another name for the same program.
15 | .\" Use .Nm macro to designate other names for the documented program.
16 | .Nd This line parsed for whatis database.
17 | .Sh SYNOPSIS \" Section Header - required - don't modify
18 | .Nm
19 | .Op Fl abcd \" [-abcd]
20 | .Op Fl a Ar path \" [-a path]
21 | .Op Ar file \" [file]
22 | .Op Ar \" [file ...]
23 | .Ar arg0 \" Underlined argument - use .Ar anywhere to underline
24 | arg2 ... \" Arguments
25 | .Sh DESCRIPTION \" Section Header - required - don't modify
26 | Use the .Nm macro to refer to your program throughout the man page like such:
27 | .Nm
28 | Underlining is accomplished with the .Ar macro like this:
29 | .Ar underlined text .
30 | .Pp \" Inserts a space
31 | A list of items with descriptions:
32 | .Bl -tag -width -indent \" Begins a tagged list
33 | .It item a \" Each item preceded by .It macro
34 | Description of item a
35 | .It item b
36 | Description of item b
37 | .El \" Ends the list
38 | .Pp
39 | A list of flags and their descriptions:
40 | .Bl -tag -width -indent \" Differs from above in tag removed
41 | .It Fl a \"-a flag as a list item
42 | Description of -a flag
43 | .It Fl b
44 | Description of -b flag
45 | .El \" Ends the list
46 | .Pp
47 | .\" .Sh ENVIRONMENT \" May not be needed
48 | .\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1
49 | .\" .It Ev ENV_VAR_1
50 | .\" Description of ENV_VAR_1
51 | .\" .It Ev ENV_VAR_2
52 | .\" Description of ENV_VAR_2
53 | .\" .El
54 | .Sh FILES \" File used or created by the topic of the man page
55 | .Bl -tag -width "/Users/joeuser/Library/really_long_file_name" -compact
56 | .It Pa /usr/share/file_name
57 | FILE_1 description
58 | .It Pa /Users/joeuser/Library/really_long_file_name
59 | FILE_2 description
60 | .El \" Ends the list
61 | .\" .Sh DIAGNOSTICS \" May not be needed
62 | .\" .Bl -diag
63 | .\" .It Diagnostic Tag
64 | .\" Diagnostic informtion here.
65 | .\" .It Diagnostic Tag
66 | .\" Diagnostic informtion here.
67 | .\" .El
68 | .Sh SEE ALSO
69 | .\" List links in ascending order by section, alphabetically within a section.
70 | .\" Please do not reference files that do not exist without filing a bug report
71 | .Xr a 1 ,
72 | .Xr b 1 ,
73 | .Xr c 1 ,
74 | .Xr a 2 ,
75 | .Xr b 2 ,
76 | .Xr a 3 ,
77 | .Xr b 3
78 | .\" .Sh BUGS \" Document known, unremedied bugs
79 | .\" .Sh HISTORY \" Document history if command behaves in a unique manner
--------------------------------------------------------------------------------
/code/structure-of-arrays/activate/activate.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 296232521830ACD100407933 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 296232511830ACD100407933 /* main.cpp */; };
11 | /* End PBXBuildFile section */
12 |
13 | /* Begin PBXCopyFilesBuildPhase section */
14 | 296232431830ACB800407933 /* CopyFiles */ = {
15 | isa = PBXCopyFilesBuildPhase;
16 | buildActionMask = 2147483647;
17 | dstPath = /usr/share/man/man1/;
18 | dstSubfolderSpec = 0;
19 | files = (
20 | );
21 | runOnlyForDeploymentPostprocessing = 1;
22 | };
23 | /* End PBXCopyFilesBuildPhase section */
24 |
25 | /* Begin PBXFileReference section */
26 | 296232451830ACB800407933 /* activate */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = activate; sourceTree = BUILT_PRODUCTS_DIR; };
27 | 296232511830ACD100407933 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; };
28 | 296232531830ACED00407933 /* utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = utils.h; path = ../shared/utils.h; sourceTree = ""; };
29 | /* End PBXFileReference section */
30 |
31 | /* Begin PBXFrameworksBuildPhase section */
32 | 296232421830ACB800407933 /* Frameworks */ = {
33 | isa = PBXFrameworksBuildPhase;
34 | buildActionMask = 2147483647;
35 | files = (
36 | );
37 | runOnlyForDeploymentPostprocessing = 0;
38 | };
39 | /* End PBXFrameworksBuildPhase section */
40 |
41 | /* Begin PBXGroup section */
42 | 2962323C1830ACB800407933 = {
43 | isa = PBXGroup;
44 | children = (
45 | 296232531830ACED00407933 /* utils.h */,
46 | 296232511830ACD100407933 /* main.cpp */,
47 | 296232461830ACB800407933 /* Products */,
48 | );
49 | sourceTree = "";
50 | };
51 | 296232461830ACB800407933 /* Products */ = {
52 | isa = PBXGroup;
53 | children = (
54 | 296232451830ACB800407933 /* activate */,
55 | );
56 | name = Products;
57 | sourceTree = "";
58 | };
59 | /* End PBXGroup section */
60 |
61 | /* Begin PBXNativeTarget section */
62 | 296232441830ACB800407933 /* activate */ = {
63 | isa = PBXNativeTarget;
64 | buildConfigurationList = 2962324E1830ACB800407933 /* Build configuration list for PBXNativeTarget "activate" */;
65 | buildPhases = (
66 | 296232411830ACB800407933 /* Sources */,
67 | 296232421830ACB800407933 /* Frameworks */,
68 | 296232431830ACB800407933 /* CopyFiles */,
69 | );
70 | buildRules = (
71 | );
72 | dependencies = (
73 | );
74 | name = activate;
75 | productName = activate;
76 | productReference = 296232451830ACB800407933 /* activate */;
77 | productType = "com.apple.product-type.tool";
78 | };
79 | /* End PBXNativeTarget section */
80 |
81 | /* Begin PBXProject section */
82 | 2962323D1830ACB800407933 /* Project object */ = {
83 | isa = PBXProject;
84 | attributes = {
85 | LastUpgradeCheck = 0500;
86 | ORGANIZATIONNAME = "Bob Nystrom";
87 | };
88 | buildConfigurationList = 296232401830ACB800407933 /* Build configuration list for PBXProject "activate" */;
89 | compatibilityVersion = "Xcode 3.2";
90 | developmentRegion = English;
91 | hasScannedForEncodings = 0;
92 | knownRegions = (
93 | en,
94 | );
95 | mainGroup = 2962323C1830ACB800407933;
96 | productRefGroup = 296232461830ACB800407933 /* Products */;
97 | projectDirPath = "";
98 | projectRoot = "";
99 | targets = (
100 | 296232441830ACB800407933 /* activate */,
101 | );
102 | };
103 | /* End PBXProject section */
104 |
105 | /* Begin PBXSourcesBuildPhase section */
106 | 296232411830ACB800407933 /* Sources */ = {
107 | isa = PBXSourcesBuildPhase;
108 | buildActionMask = 2147483647;
109 | files = (
110 | 296232521830ACD100407933 /* main.cpp in Sources */,
111 | );
112 | runOnlyForDeploymentPostprocessing = 0;
113 | };
114 | /* End PBXSourcesBuildPhase section */
115 |
116 | /* Begin XCBuildConfiguration section */
117 | 2962324C1830ACB800407933 /* Debug */ = {
118 | isa = XCBuildConfiguration;
119 | buildSettings = {
120 | ALWAYS_SEARCH_USER_PATHS = NO;
121 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
122 | CLANG_CXX_LIBRARY = "libc++";
123 | CLANG_ENABLE_OBJC_ARC = YES;
124 | CLANG_WARN_BOOL_CONVERSION = YES;
125 | CLANG_WARN_CONSTANT_CONVERSION = YES;
126 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
127 | CLANG_WARN_EMPTY_BODY = YES;
128 | CLANG_WARN_ENUM_CONVERSION = YES;
129 | CLANG_WARN_INT_CONVERSION = YES;
130 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
131 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
132 | COPY_PHASE_STRIP = NO;
133 | GCC_C_LANGUAGE_STANDARD = gnu99;
134 | GCC_DYNAMIC_NO_PIC = NO;
135 | GCC_ENABLE_OBJC_EXCEPTIONS = YES;
136 | GCC_OPTIMIZATION_LEVEL = 0;
137 | GCC_PREPROCESSOR_DEFINITIONS = (
138 | "DEBUG=1",
139 | "$(inherited)",
140 | );
141 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
142 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
143 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
144 | GCC_WARN_UNDECLARED_SELECTOR = YES;
145 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
146 | GCC_WARN_UNUSED_FUNCTION = YES;
147 | GCC_WARN_UNUSED_VARIABLE = YES;
148 | MACOSX_DEPLOYMENT_TARGET = 10.8;
149 | ONLY_ACTIVE_ARCH = YES;
150 | SDKROOT = macosx;
151 | };
152 | name = Debug;
153 | };
154 | 2962324D1830ACB800407933 /* Release */ = {
155 | isa = XCBuildConfiguration;
156 | buildSettings = {
157 | ALWAYS_SEARCH_USER_PATHS = NO;
158 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
159 | CLANG_CXX_LIBRARY = "libc++";
160 | CLANG_ENABLE_OBJC_ARC = YES;
161 | CLANG_WARN_BOOL_CONVERSION = YES;
162 | CLANG_WARN_CONSTANT_CONVERSION = YES;
163 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
164 | CLANG_WARN_EMPTY_BODY = YES;
165 | CLANG_WARN_ENUM_CONVERSION = YES;
166 | CLANG_WARN_INT_CONVERSION = YES;
167 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
168 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
169 | COPY_PHASE_STRIP = YES;
170 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
171 | ENABLE_NS_ASSERTIONS = NO;
172 | GCC_C_LANGUAGE_STANDARD = gnu99;
173 | GCC_ENABLE_OBJC_EXCEPTIONS = YES;
174 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
175 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
176 | GCC_WARN_UNDECLARED_SELECTOR = YES;
177 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
178 | GCC_WARN_UNUSED_FUNCTION = YES;
179 | GCC_WARN_UNUSED_VARIABLE = YES;
180 | MACOSX_DEPLOYMENT_TARGET = 10.8;
181 | SDKROOT = macosx;
182 | };
183 | name = Release;
184 | };
185 | 2962324F1830ACB800407933 /* Debug */ = {
186 | isa = XCBuildConfiguration;
187 | buildSettings = {
188 | PRODUCT_NAME = "$(TARGET_NAME)";
189 | };
190 | name = Debug;
191 | };
192 | 296232501830ACB800407933 /* Release */ = {
193 | isa = XCBuildConfiguration;
194 | buildSettings = {
195 | PRODUCT_NAME = "$(TARGET_NAME)";
196 | };
197 | name = Release;
198 | };
199 | /* End XCBuildConfiguration section */
200 |
201 | /* Begin XCConfigurationList section */
202 | 296232401830ACB800407933 /* Build configuration list for PBXProject "activate" */ = {
203 | isa = XCConfigurationList;
204 | buildConfigurations = (
205 | 2962324C1830ACB800407933 /* Debug */,
206 | 2962324D1830ACB800407933 /* Release */,
207 | );
208 | defaultConfigurationIsVisible = 0;
209 | defaultConfigurationName = Release;
210 | };
211 | 2962324E1830ACB800407933 /* Build configuration list for PBXNativeTarget "activate" */ = {
212 | isa = XCConfigurationList;
213 | buildConfigurations = (
214 | 2962324F1830ACB800407933 /* Debug */,
215 | 296232501830ACB800407933 /* Release */,
216 | );
217 | defaultConfigurationIsVisible = 0;
218 | };
219 | /* End XCConfigurationList section */
220 | };
221 | rootObject = 2962323D1830ACB800407933 /* Project object */;
222 | }
223 |
--------------------------------------------------------------------------------
/code/structure-of-arrays/activate/main.cpp:
--------------------------------------------------------------------------------
1 | #include "utils.h"
2 |
3 | static const int NUM_FRAMES = 1000;
4 | static const int NUM_ACTORS = 10000;
5 | static const int NUM_FIELDS = 4;
6 | static const int TOGGLE_RATE = 10;
7 |
8 | class Actor
9 | {
10 | public:
11 | Actor()
12 | {
13 | for (int i = 0; i < NUM_FIELDS; i++) data[i] = i;
14 | }
15 |
16 | bool isActive = false;
17 |
18 | int data[NUM_FIELDS];
19 |
20 | int update()
21 | {
22 | int sum = 0;
23 | for (int i = 0; i < NUM_FIELDS; i++) sum += data[i];
24 | return sum;
25 | }
26 | };
27 |
28 | class SmallActor
29 | {
30 | public:
31 | SmallActor()
32 | {
33 | for (int i = 0; i < NUM_FIELDS; i++) data[i] = i;
34 | }
35 |
36 | int data[NUM_FIELDS];
37 |
38 | int update()
39 | {
40 | int sum = 0;
41 | for (int i = 0; i < NUM_FIELDS; i++) sum += data[i];
42 | return sum;
43 | }
44 | };
45 |
46 | // A flat array of contiguous actors with randomly chosen ones active.
47 | void testMixed()
48 | {
49 | Actor* actors = new Actor[NUM_ACTORS];
50 |
51 | // Enable half of them randomly.
52 | int* indexes = shuffledArray(NUM_ACTORS);
53 | for (int i = 0; i < NUM_ACTORS / 2; i++)
54 | {
55 | actors[indexes[i]].isActive = true;
56 | }
57 | delete [] indexes;
58 |
59 | long sum = 0;
60 | startProfile();
61 | for (int i = 0; i < NUM_FRAMES; i++)
62 | {
63 | for (int j = 0; j < NUM_ACTORS; j++)
64 | {
65 | if (actors[j].isActive)
66 | {
67 | sum += actors[j].update();
68 | }
69 |
70 | // Randomly change active state.
71 | if (randRange(1, TOGGLE_RATE) == 0) actors[j].isActive = !actors[j].isActive;
72 | }
73 | }
74 | endProfile(" mixed");
75 | use(sum);
76 |
77 | delete [] actors;
78 | }
79 |
80 | // A flat array of contiguous actors with the active ones first.
81 | void testSorted()
82 | {
83 | SmallActor* actors = new SmallActor[NUM_ACTORS];
84 |
85 | // Enable half of them.
86 | int numActive = NUM_ACTORS / 2;
87 |
88 | long sum = 0;
89 | startProfile();
90 | for (int i = 0; i < NUM_FRAMES; i++)
91 | {
92 | for (int j = 0; j < numActive; j++)
93 | {
94 | sum += actors[j].update();
95 | }
96 |
97 | for (int j = 0; j < NUM_ACTORS; j++)
98 | {
99 | // Randomly change active state.
100 | if (randRange(1, TOGGLE_RATE) == 0)
101 | {
102 | if (j < numActive)
103 | {
104 | // Inactive, so move it after all of the active ones.
105 | numActive--;
106 | }
107 | else
108 | {
109 | // Active, so move it to the end of the active section.
110 | numActive++;
111 | }
112 | SmallActor temp = actors[j];
113 | actors[j] = actors[numActive];
114 | actors[numActive] = temp;
115 | }
116 | }
117 | }
118 | endProfile("sorted");
119 | use(sum);
120 |
121 | delete [] actors;
122 | }
123 |
124 | int main(int argc, const char * argv[])
125 | {
126 | srand((unsigned int)time(0));
127 |
128 | for (int i = 0; i < 4; i++)
129 | {
130 | testMixed();
131 | testSorted();
132 | }
133 |
134 | return 0;
135 | }
136 |
137 |
--------------------------------------------------------------------------------
/code/structure-of-arrays/combined/main.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // main.cpp
3 | // combined
4 | //
5 | // Created by Bob Nystrom on 11/17/13.
6 | // Copyright (c) 2013 Bob Nystrom. All rights reserved.
7 | //
8 |
9 | #include
10 |
11 | #include "utils.h"
12 |
13 | static const int NUM_FIELDS = 4;
14 | static const int NUM_ACTORS = 2000;
15 | static const int NUM_COMPONENTS = 4;
16 | static const int NUM_FRAMES = 10000;
17 | static const int NUM_COLD_FIELDS = 16;
18 | static const int EXTRA = 10;
19 |
20 | class Component
21 | {
22 | public:
23 | int data[NUM_FIELDS];
24 |
25 | Component()
26 | {
27 | for (int i = 0; i < NUM_FIELDS; i++) data[i] = i;
28 | }
29 |
30 | int update()
31 | {
32 | int sum = 0;
33 | for (int i = 0; i < NUM_FIELDS; i++) sum += data[i];
34 | return sum;
35 | }
36 | };
37 |
38 | class ActorBefore
39 | {
40 | public:
41 | bool isActive = true;
42 |
43 | Component* components[NUM_COMPONENTS];
44 | int cold[NUM_COLD_FIELDS];
45 | };
46 |
47 | void testSlow(float best)
48 | {
49 | ActorBefore** actors = new ActorBefore*[NUM_ACTORS * EXTRA];
50 |
51 | // Create a bunch of actors with random amounts of space between them.
52 | for (int i = 0; i < NUM_ACTORS * EXTRA; i++)
53 | {
54 | actors[i] = new ActorBefore();
55 | }
56 |
57 | // Shuffle them in the list.
58 | shuffle(actors, NUM_ACTORS * EXTRA);
59 |
60 | // Deactivate half of them.
61 | for (int i = 0; i < NUM_ACTORS * EXTRA / 2; i++)
62 | {
63 | actors[i]->isActive = false;
64 | }
65 |
66 | // Shuffle them in the list.
67 | shuffle(actors, NUM_ACTORS * EXTRA);
68 |
69 | // Create a bunch of components.
70 | Component** components = new Component*[NUM_ACTORS * NUM_COMPONENTS * EXTRA];
71 | for (int i = 0; i < NUM_ACTORS * NUM_COMPONENTS * EXTRA; i++)
72 | {
73 | components[i] = new Component();
74 | }
75 |
76 | // Shuffle them in the list.
77 | shuffle(components, NUM_ACTORS * NUM_COMPONENTS * EXTRA);
78 |
79 | // Wire them up to actors.
80 | for (int i = 0; i < NUM_ACTORS; i++)
81 | {
82 | for (int j = 0; j < NUM_COMPONENTS; j++)
83 | {
84 | actors[i]->components[j] = components[i * NUM_COMPONENTS + j];
85 | }
86 | }
87 |
88 | // Run the simulation.
89 | startProfile();
90 | int sum = 0;
91 | for (int f = 0; f < NUM_FRAMES; f++)
92 | {
93 | for (int c = 0; c < NUM_COMPONENTS; c++)
94 | {
95 | for (int a = 0; a < NUM_ACTORS; a++)
96 | {
97 | if (!actors[a]->isActive) continue;
98 | sum += actors[a]->components[c]->update();
99 | }
100 | }
101 | }
102 | endProfile("worst", best);
103 | if (sum != 240000000) printf("%d\n", sum);
104 |
105 | // Free everything.
106 | for (int i = 0; i < NUM_ACTORS; i++)
107 | {
108 | delete actors[i];
109 | }
110 |
111 | for (int i = 0; i < NUM_ACTORS * NUM_COMPONENTS; i++)
112 | {
113 | delete components[i];
114 | }
115 |
116 | delete [] actors;
117 | delete [] components;
118 | }
119 |
120 | float testFast()
121 | {
122 | Component* components[NUM_COMPONENTS];
123 |
124 | // Create the component arrays.
125 | for (int i = 0; i < NUM_COMPONENTS; i++)
126 | {
127 | components[i] = new Component[NUM_ACTORS];
128 | }
129 |
130 | // Run the simulation.
131 | startProfile();
132 | int sum = 0;
133 | for (int f = 0; f < NUM_FRAMES; f++)
134 | {
135 | for (int c = 0; c < NUM_COMPONENTS; c++)
136 | {
137 | Component* componentArray = components[c];
138 | for (int a = 0; a < NUM_ACTORS / 2; a++)
139 | {
140 | sum += componentArray[a].update();
141 | }
142 | }
143 | }
144 | float result = endProfile("best");
145 | if (sum != 240000000) printf("%d\n", sum);
146 |
147 | // Free everything.
148 | for (int i = 0; i < NUM_COMPONENTS; i++)
149 | {
150 | delete [] components[i];
151 | }
152 |
153 | return result;
154 | }
155 |
156 | int main(int argc, const char * argv[])
157 | {
158 | for (int i = 0; i < 3; i++)
159 | {
160 | float best = testFast();
161 | testSlow(best);
162 | }
163 | return 0;
164 | }
165 |
166 |
--------------------------------------------------------------------------------
/code/structure-of-arrays/random_array_access/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include "utils.h"
4 |
5 | // Based on http://stackoverflow.com/questions/1126529/what-is-the-cost-of-an-l1-cache-miss
6 | //
7 | // Tries to show effects of cache misses by modifying elements of an array
8 | // twice, once in order and once in random order.
9 | //
10 | // On my machine, I get results like:
11 | // ordered: 43.9250ms
12 | // random: 308.4460ms
13 | // 7.02x
14 | // ordered: 14.6060ms
15 | // random: 314.2920ms
16 | // 21.52x
17 | // ordered: 14.3640ms
18 | // random: 304.5980ms
19 | // 21.21x
20 | // ordered: 14.3160ms
21 | // random: 316.0700ms
22 | // 22.08x
23 | // ordered: 15.6090ms
24 | // random: 317.1620ms
25 | // 20.32x
26 | // ordered: 15.7320ms
27 | // random: 281.0440ms
28 | // 17.86x
29 |
30 | void accessRandomArray(int iterations, int length)
31 | {
32 | srand((unsigned int)time(0));
33 |
34 | // Allocate the data as one single chunk of memory.
35 | int* nodes = new int[length];
36 |
37 | // Create array that give the access order for the data. Going through the
38 | // indirect for both the ordered and random traversals ensures the randomness
39 | // itself doesn't add any overhead.
40 | int* orderedIndexes = new int[length];
41 | for (int i = 0; i < length; i++)
42 | {
43 | orderedIndexes[i] = i;
44 | }
45 |
46 | int* randomIndexes = shuffledArray(length);
47 |
48 | long sum = 0;
49 |
50 | // Run the whole thing a few times to make sure the numbers are stable.
51 | for (int a = 0; a < iterations; a++)
52 | {
53 | // Write the data in order.
54 | startProfile();
55 | for (int i = 0; i < length; i++) nodes[orderedIndexes[i]] = i;
56 | float ordered = endProfile("ordered: ");
57 |
58 | // Actually use the data so the compiler doesn't optimize it to nothing.
59 | for (int i = 0; i < iterations; i++) sum += nodes[i];
60 |
61 | // Now access the array positions in a "random" order.
62 | startProfile();
63 | for (int i = 0; i < length; i++) nodes[randomIndexes[i]] = i;
64 | endProfile(" random: ", ordered);
65 |
66 | // Actually use the data so the compiler doesn't optimize it to nothing.
67 | for (int i = 0; i < iterations; i++) sum -= nodes[i];
68 | }
69 |
70 | // Actually use the data so the compiler doesn't optimize it to nothing.
71 | if (sum != 0) printf("Got wrong sum %ld (should be 0)\n", sum);
72 |
73 | delete [] nodes;
74 | delete [] orderedIndexes;
75 | delete [] randomIndexes;
76 | }
77 |
78 | int main( int argc, char* argv[] )
79 | {
80 | accessRandomArray(6, 20000000);
81 | }
--------------------------------------------------------------------------------
/code/structure-of-arrays/shared/utils.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | clock_t startTime;
9 |
10 | void startProfile()
11 | {
12 | startTime = clock();
13 | }
14 |
15 | float endProfile(const char* message)
16 | {
17 | float elapsed = (float)(clock() - startTime) * 1000.0f / CLOCKS_PER_SEC;
18 | printf("%s%10.4fms\n", message, elapsed);
19 | return elapsed;
20 | }
21 |
22 | float endProfile(const char* message, float comparison)
23 | {
24 | float elapsed = (float)(clock() - startTime) * 1000.0f / CLOCKS_PER_SEC;
25 | printf("%s%10.4fms %6.2fx\n", message, elapsed, elapsed / comparison);
26 | return elapsed;
27 | }
28 |
29 | // Get random value in the given range (half-inclusive).
30 | int randRange(int m, int n)
31 | {
32 | return rand() % (n - m) + m;
33 | }
34 |
35 | // Make sure the code leading to the argument to this call isn't compiled out.
36 | void use(long sum)
37 | {
38 | if (sum == 123) printf("!");
39 | }
40 |
41 | template
42 | T median(std::vector items)
43 | {
44 | std::sort(items.begin(), items.end());
45 | if (items.size() % 2 == 0)
46 | {
47 | return (items[items.size() / 2] + items[items.size() / 2 + 1]) / 2;
48 | }
49 | else
50 | {
51 | return items[items.size() / 2];
52 | }
53 | }
54 |
55 | int* shuffledArray(int length)
56 | {
57 | int* array = new int[length];
58 | for (int i = 0; i < length; i++) array[i] = i;
59 |
60 | for (int i = 0; i < length; i++)
61 | {
62 | int j = randRange(i, length);
63 | int t = array[i];
64 | array[i] = array[j];
65 | array[j] = t;
66 | }
67 |
68 | return array;
69 | }
70 |
71 | template
72 | void shuffle(T* array, int length)
73 | {
74 | for (int i = 0; i < length; i++)
75 | {
76 | int j = randRange(i, length);
77 | T temp = array[i];
78 | array[i] = array[j];
79 | array[j] = temp;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/draft/outline/context-parameter.markdown:
--------------------------------------------------------------------------------
1 | ^title Context Parameter
2 | ^theme Behaving
3 | ^outline
4 |
5 | ## Intent
6 | *Define behavior in terms of operations on a given context object
7 | passed to the method.*
8 |
9 | ## Motivation
10 | Each entity defines a Render() function to draw the entity. In a
11 | naïve implementation, would call low-level render API directly.
12 | But that couples entity code to entire render API, some of which may
13 | be too low-level.
14 |
15 | Worse, in order to render, need to know right state. For example,
16 | local transform of entity, camera position, which buffer to render to,
17 | etc.
18 |
19 | We need a way to bundle this contextual information and have entity
20 | implement its rendering in terms of that context.
21 |
22 | ## The Pattern
23 | **Base class** defines a **contextual operation** as an abstract
24 | method that takes a **context object**. Context object maintains state
25 | needed to perform operations, and exposes **basic operations** that
26 | use it. Derived **concrete class** implements the contextual operation
27 | by calling methods on the context object.
28 |
29 | ## When to Use It
30 | * When the context and operations needed aren't already available
31 | either globally (see Service) or from the base class itself (see
32 | Subclass Sandbox).
33 |
34 | * When the context needed to perform an operation is not owned by the
35 | class defining the operation. (In our example, Entity doesn't own
36 | the render state.)
37 |
38 | ## Keep in Mind
39 |
40 | ## Design Decisions
41 |
42 | ## Sample Code
43 |
44 | ## See Also
45 |
46 | * Sandbox works similarly, but used protected methods in base class
47 | instead of passed in object.
--------------------------------------------------------------------------------
/draft/outline/game-loop.markdown:
--------------------------------------------------------------------------------
1 | ^title Game Loop
2 | ^section Sequencing Patterns
3 | ^outline
4 |
5 | ## Intent
6 |
7 | *Simulate the progression of time and run smoothly independent of user input.*
8 |
9 | ## Motivation
10 |
11 | - old programs used to be batch: ran and quit
12 | - then came interactive programs. these ran forever, which meant loop
13 | - if you write gui app, the os has event loop
14 | - you receive ui event, handle it, and return to event loop
15 | - js is a great example
16 | - you don't see loop, but it's there
17 | - when app isn't handle user input, it isn't doing anything, just sitting
18 | there
19 | - this can work for simple games (minesweeper, solitaire)
20 | - game loop
21 | - games are living breathing worlds. need to keep simulating independent of
22 | user input. don't wait.
23 | - even turn based games often have animation that needs to keep going even
24 | when user isn't doing anything
25 | - solution is game does loop itself. polls for input instead of blocking
26 |
27 | ### Two Kinds of Time
28 |
29 | - two kinds of time: user's wall clock time. game's simulation time (ticks)
30 | - exchange rate between them: how much *work* can the cpu handle in a given
31 | amount of real time. old games could hard code exchange rate.
32 | - modern hardware is too flexible: run on range of devices, multitasking,
33 | battery life, etc.
34 | - need to dynamically calculate exchange rate
35 |
36 | - out of scope
37 | - networking and coordinating time
38 | - threading: sim on one, render on another, etc.
39 |
40 | ## The Pattern
41 |
42 | A **game loop** runs continuously until the game is exited. Each turn of the
43 | loop, it **reads user input**, then **updates the game state**, then **displays
44 | the game**. It will also keep track of the passage of time so that it can
45 | control the **rate of gameplay**.
46 |
47 | ## When to Use It
48 |
49 | One of the few things common to almost every game. You will almost always use
50 | it.
51 |
52 | Counterexample is non-realtime games that rely on OS GUI. If you write a
53 | Minesweeper clone, may not to code a game loop. Still there, though: it's the
54 | OS's event loop.
55 |
56 | may not write it yourself. if using game engine, engine will usually provide it. this is one of the main differences between a "library" and an "engine" or "framework". with engine, it owns the main loop and calls your code. lib is other way. game loop is drive shaft of engine.
57 |
58 | will likely even use this for turn-based games since animation, vfx, etc. is
59 | realtime. a turn-based game is just a realtime game where the enemy doesn't
60 | move until after the player does.
61 |
62 | ## Keep in Mind
63 |
64 | - need to coordinate with os' event loop
65 | - consider different hardware capabilities game will run on. the more known
66 | they are, the simpler your game loop
67 | - running as fast as possible on mobile can harm battery life
68 |
69 | ## Sample Code
70 |
71 | ### Ye Olde Game Loope
72 |
73 | main()
74 | while (notDeadYet)
75 | readInput()
76 | updateGame()
77 | render()
78 |
79 | fine in atari days where you knew exact hardware you were running on and could
80 | own entire cpu.
81 |
82 | bad these days.
83 |
84 | ### Fixing the Framerate
85 |
86 |
87 |
88 | - if machine is fast enough game runs at reliable speed
89 | - game still runs at right speed if machine is faster
90 | - battery-life friendly
91 | - game slows down if machine is too slow
92 |
93 | ### Decouple Update and Render Rate
94 |
95 |
96 |
97 | - uses max power
98 | - updates with fixed time step
99 | - can get into spiral of death
100 | - doesn't play more smoothly on better hardware
101 |
102 | ### Interpolated Rendering
103 |
104 |
105 |
106 | ## Design Decisions
107 |
108 | ### How does it adapt to fast hardware?
109 |
110 | ### How does it adapt to slow hardware?
111 |
112 | ### Does power consumption or CPU usage matter?
113 |
114 | - if the loop has sleep(), battery-friendly. if not, optimizes game experience
115 |
116 | ## See Also
117 |
118 | http://gafferongames.com/game-physics/fix-your-timestep/
119 | http://www.koonsolo.com/news/dewitters-gameloop/
120 | http://en.wikipedia.org/wiki/Game_programming
121 | http://active.tutsplus.com/tutorials/games/understanding-the-game-loop-basix/
122 | http://www.mvps.org/directx/articles/writing_the_game_loop.htm
123 | http://entropyinteractive.com/2011/02/game-engine-design-the-game-loop/
124 | http://www.nuclex.org/articles/3-basics/5-how-a-game-loop-works
125 |
--------------------------------------------------------------------------------
/draft/outline/hello-world.markdown:
--------------------------------------------------------------------------------
1 | ^title Hello World
2 |
3 | Here's a general summary of the problem or problems that this pattern
4 | can solve. It's a fairly short introductory paragraph to the pattern.
5 | Note that it lacks a header. It ends with a single *emphasized
6 | summary of the pattern*.
7 |
8 | ## The Pattern
9 | This is the main expository section of the pattern. It explains what
10 | the pattern is in terms of an example application. Although it
11 | references an example, it does not use code to explain the pattern.
12 | This is still the pattern in fairly general terms.
13 |
14 | > This is a sidebar section. These can be used anywhere in the book
15 | > to provide additional, but not necessary, information about the
16 | > following passage.
17 | >
18 | > It has two paragraphs.
19 |
20 | This section will likely be a page or two long, and will often contain
21 | diagrams or other helpful illustrations.
22 |
23 | ## Implementation
24 | This longer section walks through the important skeleton of an
25 | implementation of the example pattern described in the previous
26 | section. It will not necessarily show *all* of the code required to
27 | implement the pattern, just enough to get the idea across.
28 |
29 | Because of the step-by-step nature of this section, and the frequent
30 | code samples, this will likely be the longer part of the chapter.
31 |
32 | Just for format testing, here's an ordered list:
33 |
34 | 1. This is the first item. It has a pretty long body of text so that
35 | we can ensure that text wrapping is working correctly.
36 |
37 | * And this is a nested list. It has a pretty long body of text so
38 | that we can ensure that text wrapping is working correctly.
39 |
40 | 1. Three levels of nesting should be plenty. Make it nice and
41 | long so we can ensure word wrap wraps at the right place.
42 |
43 | * Second nested item.
44 |
45 | 2. This is the second item.
46 |
47 | 3. This is the third item.
48 |
49 | Here is one code sample:
50 |
51 | ^code 1
52 |
53 | Just to test code inclusion and ensure it's working right, here's
54 | another:
55 |
56 | ^code 2
57 |
58 | ## Implications
59 |
60 | * In bullet form, lists the implications of using this pattern.
61 |
62 | * Includes pros.
63 |
64 | * Also includes cons.
65 |
66 | ## Applications
67 | This short section describes common uses of the pattern. It may be
68 | omitted if not useful.
69 |
70 | ## Related Patterns
71 | Patterns are often used in tandem or in place of other patterns. This
72 | section, if relevant, describes other patterns that often touch on
73 | this one.
74 |
75 | **How are refinements handled? Do they get all of the sections the
76 | full pattern gets?**
77 |
--------------------------------------------------------------------------------
/draft/outline/introduction.markdown:
--------------------------------------------------------------------------------
1 | ^title Introduction
2 |
3 | * my personal history
4 | * taught myself to program
5 | * always wanted to write games, like every one else
6 | * could find resources for graphics, sound, etc. but struggled on
7 | personal projects. once they reached a certain size, they became
8 | unweildy messes and i gave up.
9 | * in 2001, got a job at ea.
10 | * couldn't wait to see how real game teams organized their
11 | codebases.
12 | * not what i was expecting: there were some architectural gems,
13 | but a lot of their codebases looked like my unweildy messes.
14 | they justthrew more bodies at the problem and soldiered on.
15 | * disappointing, but inspiring too: i know it can be done better.
16 | some parts of codebases i saw were really brilliant. why doesn't
17 | everyone know how to put things together like that?
18 | * this book tries to help that: get the smart things i saw some
19 | people doing and collect them together so that everyone else can
20 | learn from it.
21 | * what's in store
22 | * not like other game books: no graphics algorithms, etc. instead,
23 | provides so you can build an architecture to house those things.
24 | if a game is a house, graphics and sound are light fixtures and
25 | appliances. this book will teach you how to put in the wiring
26 | and plumbing.
27 | * this won't teach you how to write an entire game engine. but if
28 | you've ever worked on a game engine and wondered why it was so
29 | hard to add features to it, or change, this book aims to help.
30 |
31 | ## How it Relates to Design Patterns
32 | * this is essentially an extension of gof's book. while you don't
33 | necessarily need both, if i were you and i could only buy one of
34 | these books, i'd buy theirs. almost every pattern in design
35 | patterns can find an effective use inside a game engine. if you're
36 | doing significant architecture on a game, you should be familiar
37 | with them.
38 | * once you have those down, this book picks up from there with more
39 | patterns that are rarely encountered outside of games.
40 | * this also covers some patterns from the gof book, but with a
41 | clearer emphasis on how they can be used in games. if you know
42 | patterns, but have trouble knowing when to use them, this may
43 | help.
44 | * some patterns here are smaller in scale. design patterns tries
45 | to restrict itself to just architectural patterns. this book
46 | has those, but also smaller scale implementation patterns.
47 |
48 | ## How This Book is Structured and Formatted
49 |
50 | * overview and intro struff
51 | * if this were a recipe book (and it is), this would be how to
52 | measure ingredients, cut meat, etc.
53 | * how the book is intended to be used
54 | * how it relates to other material on patterns
55 | * how to apply it to your everyday programming
56 | * collection of patterns
57 | * these are the recipes
58 | * patterns are grouped into several themes. each theme describes
59 | two opposing forces that cause problems which must be reconciled
60 | to make a game. for each theme, several design patterns are
61 | provided.
62 | * each pattern is structured like
63 | * introduction: describes the problem the pattern solves
64 | * the pattern: using an example usage, describes each step and
65 | part of the pattern. no code samples yet.
66 | * implementation: demonstrates and explains a sample
67 | implementation of the previously described example. not all
68 | code is included, just the meat required to get the point
69 | across.
70 | * implications: the pros, cons, requirements and side-effects
71 | of using this pattern are explaining in a bullet list
72 | * applications: if useful, a short section explaining common
73 | places where this pattern is used. for example: the command
74 | pattern is often used for undo, etc.
75 | * related patterns: if the pattern is often used in
76 | conjunction with others, or otherwise references them,
77 | mention them here.
78 | * refinements: some patterns may be expanded upon or modified.
79 | if applicable, describe common refinements here. **still
80 | need to figure out exactly how this will be integrated into
81 | the overall pattern chapter.**
82 | * formatting
83 | * code is in a `monospace font`
84 | * material that may be interesting but isn't necessary to know may
85 | show up in sidebars
86 | * patterns have components. each component is in **bold face**
87 | when it is first defined
88 | * patterns described in this book are Capitalized (1) with the
89 | page number of the pattern's chapter after the name.
90 |
91 | ## Example Code
92 |
93 | * none of the patterns in this are particularly bound to a single
94 | programming language. whatever language you're using can probably
95 | handle them. so the choice of example language is kind of arbitrary.
96 | * sample code is in c++
97 | * most game programmers know it, or at least know a language like
98 | Java or C# with similar syntax
99 | * consistent with GoF
100 | * some patterns deal specifically with memory or static type
101 | system limitations, and c++ lets you work in those domains.
102 | * complete working programs are not presented here. you're a smart
103 | coder and can fill in the blanks with obvious stuff. keeps us
104 | focused on the heart of the pattern.
105 | * the coding style is one that works best for a book, not for
106 | production code. may not be the best style guide.
107 |
108 | ## Evolving
109 |
110 | * patterns by nature are constantly growing and changing to reflect
111 | the community that uses them. your involvement is an important
112 | part of the process. if you have suggestions, corrections, ideas,
113 | or problems with the what's in here, please get in touch.
--------------------------------------------------------------------------------
/draft/outline/service.markdown:
--------------------------------------------------------------------------------
1 | ^title Service
2 | ^theme Communicating
3 | ^outline
4 |
5 | ## Intent
6 | *Provide a global point of access to a service, without coupling users
7 | of the service to the concrete class that implements it.*
8 |
9 | ## Motivation
10 | Many systems in a game need to cause sounds to play. Want to provide
11 | global access to ability to play sound.
12 |
13 | Usually use a Singleton for this, but that still couples to concrete class, and also brings in an unneeded single instance limitation.
14 |
15 | Better is to separate out the class providing *access* to a service
16 | from the class *implementing* it. This way, game is only coupled to
17 | abstract service class, and not any concrete audio classes.
18 |
19 | ## The Pattern
20 | **Service Provider** class provides a globally-accessible reference to an instance of a **Service**, an interface for a set of operations. At
21 | runtime, an instance of a **Service Implementation** is registered
22 | with the provider.
23 |
24 | ## When to Use It
25 | Use it when many things will want access to a single content-independent module: audio, networking, visual effects.
26 |
27 | Very useful when there may be multiple implementations of the service.
28 |
29 | ## Keep in Mind
30 | * It's global, anything globally accessible can still cause unwanted
31 | interdependencies. Don't overuse.
32 |
33 | * Unlike Singleton, Service isn't automatically instantiated. Make
34 | sure you register a service before it's used.
35 |
36 | * Service doesn't have to be *global*. Could also limit it to a
37 | base class. For example, audio service is registered with the
38 | base Actor class, which then exposes it to all subclasses.
39 |
40 | * Have to go through v-table for calls. May not be efficient enough
41 | for services used very frequently.
42 |
43 | ## Design Decisions
44 |
45 | * What is the scope of the service (who can use it)? Global?
46 | Namespace? Class? Narrowed minimizes coupling, but can cause you
47 | to have multiple classes store reference to same service. Means
48 | you have to be careful that the service is registered everywhere
49 | it needs to be.
50 |
51 | * Who registers the service? One place in code will be coupled to
52 | both the service provider, and the concrete implementation.
53 |
54 | * What happens if service before implementation is registered?
55 |
56 | Can handle this with a **Null Service**: an implementation of the
57 | service that does nothing. Return instance of this if you try
58 | to use Service without registering implementation. Allows you to
59 | safely turn off features, such as removing audio during
60 | development.
61 |
62 | * What is the API for the service itself? This is a public interface
63 | that will be used and implemented frequently. Take care to design
64 | it well.
65 |
66 | ## Sample Code
67 |
68 | ### The Service
69 | *Show an example AudioService API, an abstract class with a couple of
70 | PlaySound() methods.*
71 |
72 | *Show an example implementation, AudioImpl (bodies of functions will
73 | not actually be implemented).*
74 |
75 | ### A Simple Provider
76 | *Show static class AudioProvider. Has methods to get and set
77 | AudioService pointer.*
78 |
79 | ### Service Unavailable
80 | *Show NullAudioImpl null service class. Show how AudioService returns
81 | a pointer to that if no other service is registered.*
82 |
83 | ### Logging Decorator
84 | Logging is useful during development, but don't want the performance
85 | hit in the shipping game. Often use #ifdefs for this, but we can also
86 | use Service and Decorator to a cleaner effect.
87 |
88 | *Show AudioLogger class that decorates AudioService by logging and
89 | then forwarding call to wrapped instance.*
90 |
91 | *Show how code registering service can conditionally wrap in logging
92 | when desired.*
93 |
94 | ## See Also
95 | *Compare to Singleton.*
--------------------------------------------------------------------------------
/draft/outline/spatial-partition.markdown:
--------------------------------------------------------------------------------
1 | ^title Spatial Partition
2 | ^section Optimizing Patterns
3 |
4 | ## Intent
5 |
6 | *Quickly find objects near a certain point or area by storing them in a data
7 | structure that organizes them by location.*
8 |
9 | ## Motivation
10 |
11 | - games inhabit virtual world. that world often mimics the real world.
12 | - one facet of that is things have a position and interact in physical space.
13 | - in other words, you can be near stuff, and stuff can collide.
14 | - obvious example is physics and collision, but other stuff too.
15 | - for online games, maybe chat messages are only heard by other players near
16 | you
17 | - only render things near the player (though visibility culling is a bit
18 | different)
19 | - audio system may take into account which objects are nearby
20 | - all of this boils down to, given a location in the game world, what's nearby
21 | - we'll pick collision as example
22 | - for collision, need to see which pairs of objects are touching.
23 | - comparing each pair in whole world is n^2!
24 | - as number of objects increases, gets worse and worse
25 | - think about hash table
26 | - hash table lets you find object with key in constant time instead of n for
27 | searching the whole collection
28 | - what if we could make a hash table where the key is its position?
29 | - this is basic idea of spatial partition: organize objects in data structure
30 | based on where they are
31 | - literal hash table with positions as keys wouldn't work, though. often need
32 | to find objects *near* a position. hash tables, by design, don't handle
33 | approximate keys well. they intentionally spread objects out so that a tiny
34 | key change is a big location change.
35 |
36 | ## The Pattern
37 |
38 | - create data structure where objects are organized based on location in game
39 | world
40 | - allow efficient queries to find objects at or near a location
41 |
42 | ## When to Use It
43 |
44 | - when objects have position
45 | - doing frequent queries to find nearby objects
46 | - there are a lot of objects
47 |
48 | ## Keep in Mind
49 |
50 | - goal is generally to reduce a n^2 operation to n or a n to 1. if number of
51 | objects is actually small, may not be worth it.
52 |
53 | - if objects can move, they have to be reorganized in the data structure.
54 | there is some overhead to this (especially with hierarchical spatial
55 | partitions) so make sure benefits outweight organization
56 |
57 | - data structure has some memory overhead. as usual, trading memory for perf.
58 |
59 | ## Sample Code
60 |
61 | - lots of spatial partition data structures. area of active research.
62 | - since we just care about pattern, we'll do the simplest one: a fixed grid.
63 | - rts. lots and lots of units running around battlefield. need to see which
64 | units are near enough to engage in melee.
65 | - naive solution
66 | - for each unit
67 | - for each other unit
68 | - skip redundant comparisons
69 | - bounding box test
70 | - see if distance < minimum
71 | - naive solution is n^2.
72 | - want to have *lots* of units and game is slowing down.
73 | - introducing grid
74 | - overlay 2d grid overworld. each cell has a fixed size.
75 | - each cell stores list of units
76 | - units whose position falls within that cell are in that cell's list
77 | - finding collisions
78 | - for each unit
79 | - find its cell
80 | - for each other unit in cell
81 | - now same as before
82 | - still seems to be n^2: two nested loops
83 | - but since there are many cells, each cell will have fewer units
84 | - neighboring cells
85 | - if near cell boundary, may be touching units whose centerpoint is in
86 | neighboring cell. need to take those into account too.
87 |
88 | ## Design Decisions
89 |
90 | - which specific spatial partition to use. lots of options: grid, bsp, etc.
91 | - does it adapt automatically to the set of objects or is it fixed like a grid?
92 | - fixed is simpler
93 | - may be faster too
94 | - adaptable works better for large levels, or with user-authored content
95 | - some are optimized for objects that don't move
96 | - are objects *only* stored in spatial partition or is there a separate direct
97 | list of them too?
98 | - former uses less memory. can be slow if you need to visit all objects and
99 | most of the spatial partition is empty.
100 | - is it hierarchical or not?
101 | - hierarchical works better when objects are non-evenly distributed ("clumpy")
102 | - flat is simpler
103 |
104 | ## See Also
105 |
106 | * http://en.wikipedia.org/wiki/Space_partitioning
107 | * http://en.wikipedia.org/wiki/Grid_(spatial_index)
108 | * http://en.wikipedia.org/wiki/Kd-tree
109 | * http://en.wikipedia.org/wiki/Binary_space_partitioning
110 | * http://en.wikipedia.org/wiki/Quad_tree
111 |
--------------------------------------------------------------------------------
/html/acknowledgements.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Acknowledgements · Game Programming Patterns
7 |
8 |
10 |
11 |
12 |
13 |
22 |
23 |
24 |
25 |
26 |
77 | © 2009-2021 Robert Nystrom
78 |
79 |
80 |
--------------------------------------------------------------------------------
/html/behavioral-patterns.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Behavioral Patterns · Game Programming Patterns
7 |
8 |
10 |
11 |
12 |
13 |
22 |
23 |
24 |
25 |
26 |
63 | © 2009-2021 Robert Nystrom
64 |
65 |
66 |
--------------------------------------------------------------------------------
/html/decoupling-patterns.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Decoupling Patterns · Game Programming Patterns
7 |
8 |
10 |
11 |
12 |
13 |
22 |
23 |
24 |
25 |
26 |
61 | © 2009-2021 Robert Nystrom
62 |
63 |
64 |
--------------------------------------------------------------------------------
/html/design-patterns-revisited.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Design Patterns Revisited · Game Programming Patterns
7 |
8 |
10 |
11 |
12 |
13 |
22 |
23 |
24 |
25 |
26 |
68 | © 2009-2021 Robert Nystrom
69 |
70 |
71 |
--------------------------------------------------------------------------------
/html/images/Thumbs.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/Thumbs.db
--------------------------------------------------------------------------------
/html/images/architecture-cycle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/architecture-cycle.png
--------------------------------------------------------------------------------
/html/images/arrow-inherits.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/arrow-inherits.png
--------------------------------------------------------------------------------
/html/images/arrow-references.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/arrow-references.png
--------------------------------------------------------------------------------
/html/images/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/background.png
--------------------------------------------------------------------------------
/html/images/bytecode-ast.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/bytecode-ast.png
--------------------------------------------------------------------------------
/html/images/bytecode-code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/bytecode-code.png
--------------------------------------------------------------------------------
/html/images/bytecode-literal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/bytecode-literal.png
--------------------------------------------------------------------------------
/html/images/bytecode-numbers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/bytecode-numbers.png
--------------------------------------------------------------------------------
/html/images/bytecode-stack-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/bytecode-stack-1.png
--------------------------------------------------------------------------------
/html/images/bytecode-stack-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/bytecode-stack-2.png
--------------------------------------------------------------------------------
/html/images/bytecode-stack-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/bytecode-stack-3.png
--------------------------------------------------------------------------------
/html/images/bytecode-ui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/bytecode-ui.png
--------------------------------------------------------------------------------
/html/images/command-buttons-one.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/command-buttons-one.png
--------------------------------------------------------------------------------
/html/images/command-buttons-two.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/command-buttons-two.png
--------------------------------------------------------------------------------
/html/images/command-stream.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/command-stream.png
--------------------------------------------------------------------------------
/html/images/command-undo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/command-undo.png
--------------------------------------------------------------------------------
/html/images/component-uml.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/component-uml.png
--------------------------------------------------------------------------------
/html/images/data-locality-cache-line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/data-locality-cache-line.png
--------------------------------------------------------------------------------
/html/images/data-locality-chart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/data-locality-chart.png
--------------------------------------------------------------------------------
/html/images/data-locality-component-arrays.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/data-locality-component-arrays.png
--------------------------------------------------------------------------------
/html/images/data-locality-pointer-chasing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/data-locality-pointer-chasing.png
--------------------------------------------------------------------------------
/html/images/data-locality-things.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/data-locality-things.png
--------------------------------------------------------------------------------
/html/images/dirty-flag-multiply.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/dirty-flag-multiply.png
--------------------------------------------------------------------------------
/html/images/dirty-flag-pirate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/dirty-flag-pirate.png
--------------------------------------------------------------------------------
/html/images/dirty-flag-title-bar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/dirty-flag-title-bar.png
--------------------------------------------------------------------------------
/html/images/dirty-flag-update-bad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/dirty-flag-update-bad.png
--------------------------------------------------------------------------------
/html/images/dirty-flag-update-good.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/dirty-flag-update-good.png
--------------------------------------------------------------------------------
/html/images/dogshot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/dogshot.jpg
--------------------------------------------------------------------------------
/html/images/double-buffer-face.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/double-buffer-face.png
--------------------------------------------------------------------------------
/html/images/double-buffer-slaps-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/double-buffer-slaps-1.png
--------------------------------------------------------------------------------
/html/images/double-buffer-slaps-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/double-buffer-slaps-2.png
--------------------------------------------------------------------------------
/html/images/double-buffer-tearing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/double-buffer-tearing.png
--------------------------------------------------------------------------------
/html/images/event-queue-central.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/event-queue-central.png
--------------------------------------------------------------------------------
/html/images/event-queue-crawl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/event-queue-crawl.png
--------------------------------------------------------------------------------
/html/images/event-queue-loop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/event-queue-loop.png
--------------------------------------------------------------------------------
/html/images/event-queue-queue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/event-queue-queue.png
--------------------------------------------------------------------------------
/html/images/event-queue-ring.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/event-queue-ring.png
--------------------------------------------------------------------------------
/html/images/flyweight-tiles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/flyweight-tiles.png
--------------------------------------------------------------------------------
/html/images/flyweight-tree-model.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/flyweight-tree-model.png
--------------------------------------------------------------------------------
/html/images/flyweight-trees.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/flyweight-trees.png
--------------------------------------------------------------------------------
/html/images/game-loop-fixed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/game-loop-fixed.png
--------------------------------------------------------------------------------
/html/images/game-loop-simple.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/game-loop-simple.png
--------------------------------------------------------------------------------
/html/images/game-loop-timeline-close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/game-loop-timeline-close.png
--------------------------------------------------------------------------------
/html/images/game-loop-timeline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/game-loop-timeline.png
--------------------------------------------------------------------------------
/html/images/object-pool-heap-fragment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/object-pool-heap-fragment.png
--------------------------------------------------------------------------------
/html/images/observer-linked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/observer-linked.png
--------------------------------------------------------------------------------
/html/images/observer-list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/observer-list.png
--------------------------------------------------------------------------------
/html/images/observer-nodes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/observer-nodes.png
--------------------------------------------------------------------------------
/html/images/observer-weasel-wielder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/observer-weasel-wielder.png
--------------------------------------------------------------------------------
/html/images/prototype-class.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/prototype-class.png
--------------------------------------------------------------------------------
/html/images/prototype-delegate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/prototype-delegate.png
--------------------------------------------------------------------------------
/html/images/prototype-hierarchies.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/prototype-hierarchies.png
--------------------------------------------------------------------------------
/html/images/prototype-object.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/prototype-object.png
--------------------------------------------------------------------------------
/html/images/prototype-spawner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/prototype-spawner.png
--------------------------------------------------------------------------------
/html/images/prototype-weapon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/prototype-weapon.png
--------------------------------------------------------------------------------
/html/images/spatial-partition-adjacent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/spatial-partition-adjacent.png
--------------------------------------------------------------------------------
/html/images/spatial-partition-battle-line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/spatial-partition-battle-line.png
--------------------------------------------------------------------------------
/html/images/spatial-partition-grid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/spatial-partition-grid.png
--------------------------------------------------------------------------------
/html/images/spatial-partition-linked-list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/spatial-partition-linked-list.png
--------------------------------------------------------------------------------
/html/images/spatial-partition-neighbors.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/spatial-partition-neighbors.png
--------------------------------------------------------------------------------
/html/images/spatial-partition-quadtree.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/spatial-partition-quadtree.png
--------------------------------------------------------------------------------
/html/images/state-flowchart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/state-flowchart.png
--------------------------------------------------------------------------------
/html/images/state-pushdown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/state-pushdown.png
--------------------------------------------------------------------------------
/html/images/type-object-breed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/type-object-breed.png
--------------------------------------------------------------------------------
/html/images/type-object-subclasses.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/type-object-subclasses.png
--------------------------------------------------------------------------------
/html/images/update-method-remove.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/update-method-remove.png
--------------------------------------------------------------------------------
/html/images/update-method-uml.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/update-method-uml.png
--------------------------------------------------------------------------------
/html/optimization-patterns.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Optimization Patterns · Game Programming Patterns
7 |
8 |
10 |
11 |
12 |
13 |
22 |
23 |
24 |
25 |
26 |
65 | © 2009-2021 Robert Nystrom
66 |
67 |
68 |
--------------------------------------------------------------------------------
/html/script.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 | $(window).resize(refreshAsides);
3 |
4 | // Since we may not have the height correct for the images, adjust the asides
5 | // too when an image is loaded.
6 | $('img').on('load', function() {
7 | refreshAsides();
8 | });
9 |
10 | // On the off chance the browser supports the new font loader API, use it.
11 | if (document.fontloader) {
12 | document.fontloader.notifyWhenFontsReady(function() {
13 | refreshAsides();
14 | });
15 | }
16 |
17 | // Lame. Just do another refresh after a second when the font is *probably*
18 | // loaded to hack around the fact that the metrics changed a bit.
19 | window.setTimeout(refreshAsides, 200);
20 |
21 | refreshAsides();
22 | });
23 |
24 | function refreshAsides() {
25 | // Don't position them if they're inline.
26 | if ($(document).width() < 800) return;
27 |
28 | // Vertically position the asides next to the span they annotate.
29 | $("aside").each(function() {
30 | var aside = $(this);
31 |
32 | // Find the span the aside should be anchored next to.
33 | var name = aside.attr("name");
34 | var span = $("span[name='" + name + "']");
35 | if (span == null) {
36 | window.console.log("Could not find span for '" + name + "'");
37 | return;
38 | }
39 |
40 | aside.offset({top: span.position().top - 3});
41 | });
42 | }
--------------------------------------------------------------------------------
/html/sequencing-patterns.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Sequencing Patterns · Game Programming Patterns
7 |
8 |
10 |
11 |
12 |
13 |
22 |
23 |
24 |
25 |
26 |
61 | © 2009-2021 Robert Nystrom
62 |
63 |
64 |
--------------------------------------------------------------------------------
/html/style.css.map:
--------------------------------------------------------------------------------
1 | <<<<<<< Updated upstream
2 | {"version":3,"sourceRoot":"","sources":["../asset/style.scss"],"names":[],"mappings":";AASA;EACE;;;AAGF;EACE;EACA;EACA;EAEA;EACA;;;AAGF;EACE;EAEA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;;AAIJ;EACE;;;AAGF;EACE;EACA;EACA;EAEA;EACA;EACA;EAGA;EAEA;;;AAGF;EACE;EAEA;EACA;;;AAOF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE,OA9FQ;EA+FR;EACA;EACA;EACA;;;AAGF;EACE,OAtGQ;EAuGR;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE,OAtHQ;EAuHR;EACA;EAEA;EAEA;;;AAIF;EACE;EAEA;EACA;;;AAIF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;;;AAGF;EACE,OAvLQ;;;AA0LV;EACE;EACA;EACA;EAEA;EACA;EACA;EACA;EAGA;;;AAGF;EACE;;;AAKF;EACE;EAGA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAGF;EACE;;;AAIJ;EACE;;;AAKA;EACE;;AAGF;EACE;;AAIF;EACE;EACA;EACA;;;AAIJ;EACE;EACA;EAEA;EACA;EACA;EACA;EACA;EAEA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;;;AAKF;EACE;EACA;EACA;EACA;;;AAIF;EACE;EAEA;EACA;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAIF;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EAEA;;AAKF;EACE;EACA;EACA;;AAGF;EAA8B;;AAE9B;EACE;EACA;EACA;;AAIF;EAAoC;EAAY;EAAY;EAA2B;EAAiB;EAAU;EAAc;;AAChI;EAA+B;EAAc;EAAyB;EAAkB;EAAY;EAAY;EAAW;;AAC3H;EAAsC;;AACtC;EAAwC;EAAe;;AACvD;EAA8B;EAAe;EAAY;EAAY;EAAoB;EAAiB;;;AAQ5G;EACE;;;AAGF;AAAA;AAAA;EAGE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAIF;EACE;EACA;EAEA;EACA;EACA;;;AAGF;EACE;EACA;;;AAIF;EAAkB,OAldR;;;AAmdV;EAAkB,OAndR;;;AAodV;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB,OA5dR;;;AA6dV;EAAkB,OA7dR;;;AAieV;EAAmB,OAjeT;;;AAoeV;EACE;IACE;;;EAGF;IACE;IACA;;;EAGF;IACE;;;EAGF;IACE;;;EAGF;IACE;;;EAGF;IACE;IACA;IACA;IACA;;;EAGF;IACE;IACA;IACA;IACA;;;EAGF;IACE;;;EAGF;IACE;;;EAGF;IACE;;;EAGF;IACE;IACA;IAGA;;;EAGF;IACE;;;EAIA;IACE;;;EAIJ;IACE;IACA;IACA;;;EAGF;IACE;;;AAKJ;EACE;IACE;;;EAGF;IACE;IACA;IACA;IAGA;IACA;IACA;IAEA;IACA;IACA;;EAEA;IACE;;;EAQF;IACE;IACA;;;EAMF;IACE;;EAGF;IACE","file":"style.css"}
3 | =======
4 | {"version":3,"sourceRoot":"","sources":["../asset/style.scss"],"names":[],"mappings":";AASA;EACE;;;AAGF;EACE;EACA;EACA;EAEA;EACA;;;AAGF;EACE;EAEA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAGF;EACE;;;AAIJ;EACE;;;AAGF;EACE;EACA;EACA;EAEA;EACA;EACA;EAGA;EAEA;;;AAGF;EACE;EAEA;EACA;;;AAOF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE,OA5FQ;EA6FR;EACA;EACA;EACA;;;AAGF;EACE,OApGQ;EAqGR;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAKF;EACE;;AAEA;EACE;;AAGF;EACE;;;AAIJ;EACE,OAlIQ;EAmIR;EACA;EAEA;EAEA;;;AAIF;EACE;EAEA;EACA;;;AAIF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAGF;EACE;;;AAIJ;EACE;EACA,aAhLK;EAiLL;;;AAGF;EACE;;;AAGF;EACE,OA3LQ;;;AA8LV;EACE;EACA;EACA;EAEA;EACA;EACA;EACA;EAGA;;;AAGF;EACE;;;AAKF;EACE;EAGA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAGF;EACE;;;AAIJ;EACE;;;AAQF;EACE;;;AAGF;AAAA;AAAA;EAGE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAIF;EACE;EACA;EAEA;EACA;EACA;;;AAIF;EAAkB,OA9SR;;;AA+SV;EAAkB,OA/SR;;;AAgTV;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB,OAxTR;;;AAyTV;EAAkB,OAzTR;;;AA6TV;EAAmB,OA7TT;;;AAgUV;EACE;IACE;;;EAGF;IACE;IACA;;;EAGF;IACE;;;EAGF;IACE;;;EAGF;IACE;;;EAGF;IACE;IACA;IACA;IACA;;;EAGF;IACE;IACA;IACA;IACA;;;EAGF;IACE;;;EAGF;IACE;;;EAGF;IACE;;;EAGF;IACE;IACA;IAGA;;;EAGF;IACE;;;EAIA;IACE;;;EAIJ;IACE;IACA;IACA;;;EAGF;IACE;;;EAGF;IACE;;;AAKJ;EACE;IACE;;;EAGF;IACE;IACA;IACA;IAGA;IACA;IACA;IAEA;IACA;IACA;;EAEA;IACE","file":"style.css"}
5 | >>>>>>> Stashed changes
6 |
--------------------------------------------------------------------------------
/note/bio.txt:
--------------------------------------------------------------------------------
1 | Robert Nystrom has programmed professionally for twenty years, about half of which is in games. During his eight years at Electronic Arts, he worked on behemoths like Madden and smaller titles like Henry Hatsworth in the Puzzling Adventure. He's shipped games on the PC, GameCube, PS2, XBox, X360, and DS, but is most proud of the tools and shared libraries he created for others to build on. He loves seeing usable, beautiful code magnify the creative ability of others.
2 |
3 | Robert lives with his wife and two daughters in Seattle where you are most likely to find him cooking for his friends and plying them with good beer.
4 |
--------------------------------------------------------------------------------
/note/description.txt:
--------------------------------------------------------------------------------
1 | The biggest challenge facing many game programmers is completing their game. Most game projects fizzle out, overwhelmed by the complexity of their own code. Game Programming Patterns tackles that exact problem. Based on years of experience in shipped AAA titles, this book collects proven patterns to untangle and optimize your game, organized as independent recipes so you can pick just the patterns you need.
2 |
3 | You will learn how to write a robust game loop, how to organize your entities using components, and take advantage of the CPUs cache to improve your performance. You'll dive deep into how scripting engines encode behavior, how quadtrees and other spatial partitions optimize your engine, and how other classic design patterns can be used in games.
4 |
--------------------------------------------------------------------------------
/note/dimensions.txt:
--------------------------------------------------------------------------------
1 | Here's some dimensions of popular programming books on Amazon:
2 |
3 | minecraft for dummies 5.4 x 8.6 x 0.3 inches
4 | touch develop 6 x 9 x 0.6 inches
5 | don't make me think 7 x 9.1 x 0.5 inches
6 | learning python 7.1 x 9.3 x 2.8 inches
7 | beginning c++ 7.2 x 9 x 1.1 inches
8 | node.js in action 7.4 x 9.2 x 0.8 inches
9 | unity 4 by example 7.5 x 9.2 x 1.2 inches
10 | html & css 7.9 x 9.6 x 1 inches
11 | invent your own games 8 x 10 x 0.9 inches
12 |
13 | Print on demand printers:
14 |
15 | lulu.com
16 | createspace.com
17 | lightningsource.com
18 | blurb.com (seems to target mostly photo books)
19 | bookbaby.com
20 |
21 | Based on those and looking at prices on Lulu, I think the best choice is:
22 |
23 | Product Line: Standard
24 | Binding: Perfect Bound Paperback
25 | Product Size: Crown Quarto (7.44 x 9.68 inch)
26 | Interior Color: Black & White
27 | Paper Quality: 60#
28 | Cover Finish: Gloss
29 | Cost per 280 page book: $9.80
30 |
31 | On CreateSpace:
32 |
33 | Interior Type: Black and White
34 | Trim Size: 7.5" x 9.25"
35 | Number of Pages: 280
36 | Quantity: 1
37 | Per Book: $4.21 each
38 |
39 |
40 | Margins:
41 |
42 | InDesign Classroom in a Book:
43 | Bottom: ~0.375"
44 | Outside: ~0.375"
45 | Top: ~0.5"
46 | Inside: ~0.875."
47 |
48 | SICP:
49 | Top: ~0.5"
50 | Outside: ~0.75"
51 | Bottom: ~0.75"
52 | Inside: ~1.0"
53 |
--------------------------------------------------------------------------------
/note/fonts.txt:
--------------------------------------------------------------------------------
1 | Body
2 | ----
3 |
4 | Maybe:
5 |
6 | Albertina
7 | Andron
8 | Bembo
9 | Berthold Baskerville Book
10 | Cala (Hoftype)
11 | http://www.myfonts.com/fonts/hoftype/cala/
12 | *Very* readable. Nice minimal thickness changes should balance well with code
13 | font. Nice ligatures. Love the italics.
14 | Caslon
15 | Century Schoolbook
16 | Dante
17 | http://www.myfonts.com/fonts/mti/dante-mt/
18 | Looks nice. Vertical "o" looks nice and modern. Italics are a bit too old
19 | style.
20 | Dolly
21 | Electra
22 | Elena
23 | http://processtypefoundry.com/fonts/elena/
24 | Nice modern serif. A bit brushstroke-y.
25 | Fenland
26 | Girando Pro
27 | Harriet Text Regular (Okay Type)
28 | Sweet Jesus, it's heavenly. May get played out in a few years. Super
29 | fashionable right now.
30 | Lyon
31 | Mercury
32 | Sabon Next
33 | http://www.linotype.com/240/sabon.html
34 | Very readable, very lucid. I think it will pair well. A bit too boring and
35 | too similar to Times New Roman.
36 | Sina Nova
37 | http://www.fontsquirrel.com/fonts/sina-nova
38 | Very nice. Similar to Cala.
39 | Sirba
40 | http://www.type-together.com/Sirba
41 | Very beautiful. Modern, a bit informal. Would match the prose well.
42 | Tundra
43 | https://www.fontfont.com/fonts/tundra
44 | Beautiful, clean, lucid. Very strong contender. Maybe a bit too square and
45 | narrow.
46 |
47 | No:
48 |
49 | Janson - Classy, but a bit thin at points.
50 | Jenson - Beautiful font but too old style for my writing.
51 | Minion - Too 90's, overdone.
52 | Scala - Too angular.
53 | Augustin
54 | http://www.myfonts.com/fonts/ludwiguebele/augustin/
55 | Pretty. Italics are nice. Tail on "y" and "j" are distinctive but not in a
56 | way that would fit well with the book. Might clash with the code font.
57 | Comenia
58 | OK. Has a matching sans, but no light weights. Regular weight is a bit heavy.
59 | Documenta - Too old style.
60 | Expo - Meh. Too... artsy? Feels like it would be good for a history text, but
61 | not my book.
62 | Iowan OS
63 | http://www.myfonts.com/fonts/bitstream/iowan-old-style/
64 | Nice. A bit plain. Diamond dots over i's.
65 | Miller Text Regular - Similar to Harriet. Very pretty, but a bit too dense.
66 | Milo Serif - Not a fan.
67 | Novel
68 | http://www.myfonts.com/fonts/atlas-font-foundry/novel-std/
69 | Clean and pretty. The italics are meh.
70 | Quadraat - "B" is WTF. Italics too angular.
71 | Stuart - Neat, but not the right character for the book.
72 | Storm Baskerville 10 - Too Victorian.
73 | Verdigris - Very beautiful, similar to Jenson and Sabon. Too old style.
74 | Yoga - Chunky.
75 |
76 | http://www.typophile.com/node/100581
77 | http://www.myfonts.com/users/pjy2ao1bxc/albums/591331/
78 |
79 | Sans
80 | ----
81 |
82 | News Gothic
83 | JAF Bernini Sans
84 |
85 | No:
86 |
87 | Franklin Gothic - Too heavy.
88 | Folio - Too wide.
89 |
--------------------------------------------------------------------------------
/note/print workflow.txt:
--------------------------------------------------------------------------------
1 | - Export to XML
2 | - Import XML
3 | - Place it in main text flow
4 | - Untag everything and remove the story structure
5 | - Pull out asides and restyle
6 | - Change first paragraph body text styles
7 | - Set chapter title and number on first page
8 | - Update section in running footer
9 | - Clean up
10 | - Make sure there aren't bits of 14.4pt line height
11 | - Format bullet lists
12 | - Including first and last items
13 | - Make sure italics in headers are correct
14 | - Add illustrations
15 | - Make 600 dpi bitmap
16 | - Save to Print/Illustrations/...
17 | - Place
18 | - Make inline anchor
19 | - Set Position to "Above Line" with 9pts of space before and 13.5pts after
20 | - Add a caption
21 | - Position asides
--------------------------------------------------------------------------------
/note/publishing.txt:
--------------------------------------------------------------------------------
1 | - Purchase 10 ISBNs
2 | Will need one for print and one for ebook, and 10 costs as much as two
3 | - Create account on Amazon Author Central
4 | Need to have book on Amazon first
5 | - Write description for book
6 | - Publisher Genever/Benning
7 |
8 | ---
9 |
10 | leanpub.com
11 | gumroad
12 | e-junkie
13 | lulu
14 | blurb
15 | https://www.softcover.io/
16 | smashwords
17 |
18 | https://news.ycombinator.com/item?id=6052075
19 | https://news.ycombinator.com/item?id=7030112
20 | https://news.ycombinator.com/item?id=632104
21 | https://www.goodreads.com/topic/show/849429-lulu-vs-createspace
22 | http://johnplogsdon.blogspot.com/2013/04/physical-books-createspace-vs-lulu-vs.html
23 |
24 | ---
25 |
26 | My advice to anyone else considering self-publishing:
27 | * Start with Amazon KDP and CreateSpace. Both services are easy and fast. It's the quickest way to test an idea.
28 | * Be prepared to iterate quickly based on reader feedback.
29 | * Consider paying $100 or $200 for a decent cover. You can find designers lurking around the online writing communities (such as kboards.com for fiction) or hire someone on oDesk. Make sure they have experience designing book covers, which will save time and frustration.
30 | * Have someone proof your manuscript. I see lots of writers who skip this step, and suffer in the ratings and reviews as a result.
31 | * Have a cover blurb and Amazon description that grabs people. Also, make sure that readers can easily find out about you, either through the product listing page (which Amazon grabs from Amazon Author Central) or your own product website.
32 | * If you want to use other platforms, Apple's iBookstore seems most promising. It's hard to set up, though. "iTunes Producer" is a very rough piece of software. However, if you've worked on iOS apps in the past at least you will be familiar with iTunes Connect, which is used to set pricing and monitor sales.
33 | * I have sold many PDFs, but I am not sure how that would work for fiction. I started with e-junkie but switched to Gumroad (3) which has a much better interface.
34 | Marketing is tough. One thing you can do once you have a print version through CreateSpace or another service, join Goodreads (a social network for people who love to read) and set up a Goodreads Giveaway (4) (a contest for your book that Goodreads runs -- usually a few hundred people sign up, and you have to send out 10 or 20 copies to winners that GR selects). It's free to set up, but you'll have to purchase and send out copies of the book to the winners of the giveaway. The advantages of this: Readers often write reviews, which are seen by other GR members. Many other people will put the book on their "to-read" list, and some will go out and buy the book right away because they don't want to wait to see if they won a copy.
35 | Good luck!
36 | 1. http://www.digitalmediamachine.com/2012/09/do-people-judge-e...
37 | 2. http://in30minutes.com/
38 | 3. https://gumroad.com/
39 | 4. http://goodreads.com/giveaway
40 |
41 | http://snook.ca/archives/writing/selling-ebook-on-amazon
42 | http://apethebook.com/
43 |
44 | ---
45 |
46 | Spent some time looking into using leanpub.com for generating the epub and mobi
47 | versions. It's pretty simple and works pretty well, if a bit slow. The landing
48 | page is nice.
49 |
50 | The biggest problem is that the output doesn't look that great. The stylsheet
51 | doesn't handle asides and large bullet points very well. They don't currently
52 | give you any way to customize this, and if I hack it myself, I can't sell it
53 | through them.
54 |
55 | Q: Does that matter?
56 |
57 | It may be simpler just to create the epub archive myself since I'm already
58 | doing much of the work for it already.
59 |
60 |
61 | ---
62 |
63 | Looked into being able to sell the epub and mobi files directly from my site.
64 | It sounds nice in theory, but I think it's not worth the trouble. Pros:
65 |
66 | - More money since a publisher won't take a cut.
67 | - People can get the book right from the site.
68 |
69 | Cons:
70 |
71 | - Technically complex to set up.
72 | - Many users don't know what to do with a raw downloaded .epub or .mobi file.
73 | Marketplaces like Kindle and iBooks handle getting it into the user's
74 | device.
75 | - May cannibalize my sales on those marketplaces. That in turn could lower the
76 | book's ranking there.
77 |
78 | All of those cons are serious, but I think the last point is the real nail in
79 | the coffin. Instead, I think I'll just have links to buy the book from a few
80 | different marketplaces.
81 |
82 | ---
83 |
84 | http://www.hxa.name/articles/content/epub-guide_hxa7241_2007.html
85 |
--------------------------------------------------------------------------------
/note/references.txt:
--------------------------------------------------------------------------------
1 | http://gamadu.com/artemis/
2 | http://html5quintus.com
3 |
4 | http://eventuallyconsistent.net/2013/08/12/messaging-as-a-programming-model-part-1/
5 | http://research.scee.net/files/presentations/gcapaustralia09/Pitfalls_of_Object_Oriented_Programming_GCAP_09.pdf
6 |
7 | angel 2d
8 |
9 | http://entity-systems.wikidot.com/
--------------------------------------------------------------------------------
/note/style.markdown:
--------------------------------------------------------------------------------
1 | ## Prose
2 |
3 | - `##` headers are title caps.
4 | - `###` subheaders are sentence caps.
5 | - "You" refers to the programmer.
6 | - The player is genderless and referred to as "they".
7 | - Ownership is expressed as "owner's thing" rather than "thing of the owner".
8 | - Contractions are fine if it helps read conversationally.
9 | - Keyboard keys have title case and are written as full words e.g. Control-Z.
10 | - Code blocks are preceded by a colon.
11 | - Bullet points may be sentence fragments but start with a cap and end with a period.
12 | - The "See Also" section consists entirely of bullet points.
13 | - Commas before conjunctions are fine.
14 | - American spelling.
15 | - References to class name, methods or any code use code format.
16 | - Italics for emphasis.
17 | - Quote block used for sentence long quotes but double quote ("") used for references what other people call something e.g. GoF call this the "subject".
18 | - Colon precedes block quote.
19 | - Punctuation appears outside quotation marks unless the punctuation is part of the quoted matter (logical punctuation).
20 | - Items in a numbered list are complete sentences with starting caps and ending period.
21 | - Use "Naïve" instead of "Naive".
22 | - Use smart quotes.
23 | - Use ` -- ` for em dashes.
24 | - Prefer present tense instead of future.
25 | - Prefer "we" over "you" or "I".
26 | - Use *O(n²)* for Big-O notation, not `O(n^2)`.
27 | - Put class name in `CodeFont` when referring to the class itself.
28 | - It's "Foo pattern", not "Foo Pattern", and the link just surrounds, "Foo".
29 | Ex: "Add a method to `Foo`."
30 | - Use normal font and lowercase when referring to objects of the class.
31 | Ex: "Create a new foo and pass it to `bar()`."
32 | - Capitalize when referring to pattern like proper noun, lowercase when
33 | referring to object implementing pattern.
34 | Ex: "Object Pool is a great pattern. An object pool will save you memory."
35 | - Design Patterns and other book titles are *italicized*.
36 | - Can end sentence before illustration with ":".
37 | - "vtable", all lowercase, no hyphen.
38 | - "on-screen" when an adjective ("the on-screen player"), "on screen" when a
39 | place ("the player is on screen").
40 | - Don't use a numbered list unless the order matters.
41 | - Do not have a subheader immediately follow a header
42 |
43 | ## Code
44 |
45 | - Class and enum names are Pascal case.
46 | - Camel case method names and properties.
47 | - Trailing underscore for fields. That way isFoo() doesn't collide with isFoo
48 | field.
49 | - No "I" for interfaces.
50 | - Do use "virtual" for overridden methods.
51 | - Use "double" for floating point variables.
52 | - If base class has virtual methods, give it a virtual destructor.
53 | - Virtual destructor comes before other definitions.
54 | - Virtual destructor {} is on the same line as the signature.
55 | - Public before private.
56 | - Last entry in enum doesn't have comma.
57 | - Cases are vertically aligned if the code is one statement long.
58 | - Each property in an initializer list gets its own line.
59 | - The empty constructor code after initializer list gets its own line and vertically aligns with initializer list colon.
60 | - Function code and signature on the same line is fine if code is one line long.
61 | - Passing mutable references is fine.
62 | - Prefer postfix increment.
63 |
64 | ## Open Questions
65 |
66 | - Q: How are switch cases indented?
67 | - Q: How much should const be used?
68 | - Q: Why use ":" before code blocks but not illustrations?
69 | - Q: Use parentheses when referring to methods/functions? `foo` or `foo()`?
70 | - Q: `true` or true?
71 | - Q: Are "enum", "struct", "int", etc. normal words or `code font`?
72 | - Q: Hide fields behind getters (better style) or allow public fields (less
73 | boilerplate)?
74 |
75 | ## Pattern Chapter Structure
76 |
77 | Each of the pattern chapters outside of the ones in "Design Patterns Revisited" has the same structure:
78 |
79 | ### Intent
80 |
81 | This is a single italicized, imperative sentence explaining the core idea behind the pattern and the problem it solves.
82 |
83 | ### Motivation
84 |
85 | This is the most narrative part of the chapter. It walks through an example problem and builds up to how the pattern solves it. It may have subheadings, illustrations, and even some code samples. It tends to be pretty large.
86 |
87 | ### The Pattern
88 |
89 | The motivation section culminates in an explanation of the pattern and how it solves the previous problem. This one- or two-paragraph section summarizes that.
90 |
91 | It's written in present tense with the components of the pattern itself as subjects. The major pieces of the pattern are in **bold face** the first time they are introduced here.
92 |
93 | ### When to Use It
94 |
95 | Now that the reader sees how the pattern is useful for one problem, this short section describes which other problems are or are not a good fit for it.
96 |
97 | It's usually only a few paragraphs long and often ends with a bullet list. It does not have any subheadings.
98 |
99 | ### Keep in Mind
100 |
101 | This expands on the previous section and describes consequences -- mostly negative -- of using the pattern. If it's short, it will just be a few paragraphs with no subheaders. When longer, it will have a single paragraph followed by a few subheaders, one for each consequence.
102 |
103 | ### Sample Code
104 |
105 | This long section walks step by step through a full implementation of the pattern. It will have paragraphs, illustrations, subheaders, and lots of code.
106 |
107 | ### Design Decisions
108 |
109 | This shows how the pattern can vary along different axes within the solution space. It has a one paragraph intro followed be a series of design decisions.
110 |
111 | Each has a subheader in the form of a question. It may be followed by a few paragraphs explaining the question.
112 |
113 | After that, a bullet list covers some possible answers to that question. Each bullet in the list starts with a one sentence answer in **bold face**, ending with a colon.
114 |
115 | Then there is a subsequent couple of paragraphs explaining the answer. This may be omitted.
116 |
117 | Often, there will then be a *nested* bullet list with some ramifications of that decision. If so, each point will be one or more paragraphs, the first of which starts with an italicized sentence.
118 |
119 | There may be code samples or illustrations anywhere in here.
120 |
121 | Some of the decision question subsections don't follow this structure. They still have a subheader in the form of a question, but the body may just be prose or follow some other format.
122 |
123 | ### See Also
124 |
125 | The easiest section. It's just a bullet list of short paragraphs containing links to other resources.
126 |
--------------------------------------------------------------------------------
/note/todo.txt:
--------------------------------------------------------------------------------
1 | - Fix prose references to hyperlinks to make them make sense
2 | - Index
3 | - Go through indexes for Intro - Singleton and use multi-page references
4 | where appropriate
5 |
--------------------------------------------------------------------------------
/watch:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | python3 script/format.py --watch $@
3 |
--------------------------------------------------------------------------------