Chapter 11 examples

This commit is contained in:
Nick Cano
2016-01-28 09:38:12 -08:00
parent fd5ff1cbf3
commit a034d3692a
10 changed files with 972 additions and 0 deletions
@@ -0,0 +1,143 @@
#include <stdio.h>
#include <Windows.h>
#include <math.h>
#include <queue>
#include <set>
#include <memory>
#include "WindowCode.h"
#define TILE_COST 1
class AStarNode;
typedef std::shared_ptr<AStarNode> AStarNodePtr;
class AStarNode
{
public:
int x, y;
int g, score;
AStarNodePtr parent;
AStarNode(int x, int y, int cost, AStarNodePtr p, int score = 0)
: x(x), y(y), g(cost), score(score), parent(p)
{}
static AStarNodePtr makePtr(int x, int y, int cost, AStarNodePtr p, int score = 0)
{
return AStarNodePtr(new AStarNode(x, y, cost, p, score));
}
int heuristic(const int destx, int desty) const
{
int xd = destx - x;
int yd = desty - y;
return abs(xd) + abs(yd);
}
void updateScore(int endx, int endy)
{
this->score = g + heuristic(endx, endy) * TILE_COST;
}
AStarNodePtr getCopy()
{
return AStarNode::makePtr(x, y, g, parent, score);
}
std::vector<AStarNodePtr> getChildren(int width, int height)
{
std::vector<AStarNodePtr> ret;
auto copy = getCopy();
if (x > 0)
ret.push_back(AStarNode::makePtr(x - 1, y, g + TILE_COST, copy));
if (y > 0)
ret.push_back(AStarNode::makePtr(x, y - 1, g + TILE_COST, copy));
if (x < width - 1)
ret.push_back(AStarNode::makePtr(x + 1, y, g + TILE_COST, copy));
if (y < height - 1)
ret.push_back(AStarNode::makePtr(x, y + 1, g + TILE_COST, copy));
return ret;
}
};
bool operator<(const AStarNodePtr &a, const AStarNodePtr &b)
{
return a->score > b->score;
}
bool operator==(const AStarNodePtr &a, const AStarNodePtr &b)
{
return a->x == b->x && a->y == b->y;
}
template<int WIDTH, int HEIGHT>
void makeList(AStarNodePtr end, std::vector<AStarNodePtr> nodes, int path[WIDTH][HEIGHT])
{
for (auto n = nodes.begin(); n != nodes.end(); n++)
path[(*n)->x][(*n)->y] = 2;
AStarNodePtr node = end;
while (node.get() != nullptr)
{
path[node->x][node->y] = 1;
node = node->parent;
}
}
template<int WIDTH, int HEIGHT, int BLOCKING>
bool doAStarSearch(
int map[WIDTH][HEIGHT],
int startx, int starty,
int endx, int endy,
int path[WIDTH][HEIGHT])
{
std::priority_queue<AStarNodePtr> frontier;
std::vector<AStarNodePtr> allNodes;
auto node = AStarNode::makePtr(startx, starty, 0, nullptr);
node->updateScore(endx, endy);
allNodes.push_back(node);
while (true)
{
if (node->x == endx && node->y == endy)
{
makeList<WIDTH, HEIGHT>(node, allNodes, path);
return true;
}
auto children = node->getChildren(WIDTH, HEIGHT);
for (auto c = children.begin(); c != children.end(); c++)
{
if (map[(*c)->x][(*c)->y] == BLOCKING)
continue;
auto found = std::find(allNodes.rbegin(), allNodes.rend(), *c);
if (found != allNodes.rend())
{
if (*found > *c)
{
(*found)->g = (*c)->g;
(*found)->parent = (*c)->parent;
(*found)->updateScore(endx, endy);
}
}
else
{
(*c)->updateScore(endx, endy);
frontier.push(*c);
allNodes.push_back(*c);
}
}
if (frontier.size() == 0)
return false;
node = frontier.top();
frontier.pop();
}
}
int main (int argc, char *argv[])
{
searchFunction = doAStarSearch<XSIZE, YSIZE, BLOCKING_TILE>;
showWindow();
return 0;
}
@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{35470A34-CBB0-41D1-A54B-E1CF3DE9A3B7}</ProjectGuid>
<RootNamespace>Chapter11_SearchAlgorithms</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="Chapter11_SearchAlgorithms.cpp" />
<ClCompile Include="DummyWindow.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="DummyWindow.h" />
<ClInclude Include="WindowCode.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Chapter11_SearchAlgorithms.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DummyWindow.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="DummyWindow.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="WindowCode.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>
+120
View File
@@ -0,0 +1,120 @@
#include "DummyWindow.h"
/*
THIS CODE EXISTS ONLY TO DISPLAY A WINDOW,
AND IS NOT RELEVANT TO THE EXAMPLE CODE
*/
std::map<HWND, DummyWindow*> DummyWindow::windowMap;
DummyWindow::DummyWindow(HINSTANCE _instance) : windowHandle(NULL), instance(_instance)
{}
DummyWindow::~DummyWindow(void)
{
DummyWindow::unregisterWindow(this, this->windowHandle);
}
bool DummyWindow::initialize()
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = this->instance;
wcex.hIcon = NULL; //LoadIcon(this->instance, MAKEINTRESOURCE(IDI_APPLICATION));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = L"gamhakwind";
wcex.hIconSm = NULL; //LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
if (!RegisterClassEx(&wcex))
{
auto err = GetLastError();
return false;
}
this->windowHandle = CreateWindowA
(
"gamhakwind",
"Game Hacking - Chapter 11 - SearchAlgorithms",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
420, 490,
NULL,
NULL,
this->instance,
NULL
);
if (this->windowHandle == NULL)
{
auto err = GetLastError();
//return false;
}
DummyWindow::registerWindow(this, this->windowHandle);
SetTimer(this->windowHandle, NULL, 50, NULL);
ShowWindow(this->windowHandle, SW_SHOW);
UpdateWindow(this->windowHandle);
return true;
}
void DummyWindow::finalize()
{
this->callbackMap.clear();
}
int32_t DummyWindow::doMessageLoop()
{
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return static_cast<int32_t>(msg.wParam);
}
void DummyWindow::setMessageHandler(UINT message, windowMessageCallbackType callback)
{
this->callbackMap[message] = callback;
}
LRESULT DummyWindow::onMessageReceived(HWND window, UINT message, WPARAM wparam, LPARAM lparam)
{
auto found = this->callbackMap.find(message);
if (found != this->callbackMap.end())
return found->second(this, message, wparam, lparam);
return DefWindowProc(window, message, wparam, lparam);
}
void DummyWindow::registerWindow(DummyWindow* dummy, HWND window)
{
DummyWindow::windowMap[window] = dummy;
}
void DummyWindow::unregisterWindow(DummyWindow* dummy, HWND window)
{
auto found = DummyWindow::windowMap.find(window);
if (found != DummyWindow::windowMap.end())
DummyWindow::windowMap.erase(found);
}
DummyWindow* DummyWindow::findWindow(HWND window)
{
auto found = DummyWindow::windowMap.find(window);
if (found != DummyWindow::windowMap.end())
return found->second;
return nullptr;
}
LRESULT DummyWindow::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
auto dummy = DummyWindow::findWindow(hWnd);
if (dummy)
return dummy->onMessageReceived(hWnd, uMsg, wParam, lParam);
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
+46
View File
@@ -0,0 +1,46 @@
#pragma once
/*
THIS CODE EXISTS ONLY TO DISPLAY A WINDOW,
AND IS NOT RELEVANT TO THE EXAMPLE CODE
*/
#include <windows.h>
#include <windowsx.h>
#include <map>
#include <functional>
#include <stdint.h>
class DummyWindow;
typedef std::function<LRESULT(DummyWindow*, UINT, WPARAM, LPARAM)> windowMessageCallbackType;
class DummyWindow
{
public:
DummyWindow(HINSTANCE _instance);
~DummyWindow(void);
bool initialize();
void finalize();
int32_t doMessageLoop();
void setMessageHandler(UINT message, windowMessageCallbackType callback);
HWND getHandle() { return this->windowHandle; }
private:
static std::map<HWND, DummyWindow*> windowMap;
static void registerWindow(DummyWindow* dummy, HWND window);
static void unregisterWindow(DummyWindow* dummy, HWND window);
static DummyWindow* findWindow(HWND window);
static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
private:
std::map<UINT, windowMessageCallbackType> callbackMap;
HWND windowHandle;
HINSTANCE instance;
LRESULT onMessageReceived(HWND window, UINT message, WPARAM wparam, LPARAM lparam);
};
+247
View File
@@ -0,0 +1,247 @@
#pragma once
/*
THIS CODE EXISTS ONLY TO DISPLAY A WINDOW,
AND IS NOT RELEVANT TO THE EXAMPLE CODE
*/
#include "DummyWindow.h"
#include <Windows.h>
#include <windowsx.h>
#include <memory>
#include <functional>
const static int TILESIZE = 20;
const static int OFFSETX = 0;
const static int OFFSETY = 60;
const static int YSIZE = 20;
const static int XSIZE = 20;
const static int UNBLOCKING_TILE = 0;
const static int BLOCKING_TILE = 1;
const static int START_TILE = 2;
const static int END_TILE = 3;
int map[YSIZE][XSIZE] = {};
int path[YSIZE][XSIZE] = {};
std::function<bool(
int map[YSIZE][XSIZE],
int startx, int starty,
int endx, int endy,
int path[YSIZE][XSIZE])> searchFunction;
#define DUMMY_SET_MESSAGE_HANDLER(dummy, message, callback) \
do { dummy->setMessageHandler(message, callback); } while(0)
typedef std::shared_ptr<DummyWindow> DummyWindowSharedPtr;
DummyWindowSharedPtr dummyWindow;
void clearPath()
{
memset((void*)&path, 0, sizeof(path));
}
LRESULT keyUpHandler(DummyWindow* dummy, UINT message, WPARAM wparam, LPARAM lparam)
{
if (wparam == VK_RETURN)
{
clearPath();
searchFunction(map, 0, 10, 19, 10, path);
}
return DefWindowProc(dummy->getHandle(), message, wparam, lparam);
}
LRESULT mouseUpHandler(DummyWindow* dummy, UINT message, WPARAM wparam, LPARAM lparam)
{
POINT mousePoint;
mousePoint.x = GET_X_LPARAM(lparam) - OFFSETX;
mousePoint.y = GET_Y_LPARAM(lparam) - OFFSETY;
mousePoint.x /= TILESIZE;
mousePoint.y /= TILESIZE;
mousePoint.x = mousePoint.x > XSIZE ? XSIZE : mousePoint.x;
mousePoint.y = mousePoint.y > YSIZE ? YSIZE : mousePoint.y;
mousePoint.x = mousePoint.x < 0 ? 0 : mousePoint.x;
mousePoint.y = mousePoint.y < 0 ? 0 : mousePoint.y;
if (map[mousePoint.x][mousePoint.y] == BLOCKING_TILE)
map[mousePoint.x][mousePoint.y] = UNBLOCKING_TILE;
else if (map[mousePoint.x][mousePoint.y] == UNBLOCKING_TILE)
map[mousePoint.x][mousePoint.y] = BLOCKING_TILE;
clearPath();
return DefWindowProc(dummy->getHandle(), message, wparam, lparam);
}
void paintString(HDC hdc, const wchar_t* string, int x, int y)
{
TextOut(hdc, x, y, string, wcslen(string));
}
void paintMap(HDC hdc)
{
static HBRUSH blackBrush = CreateSolidBrush(RGB(0, 0, 0));
static HBRUSH redBrush = CreateSolidBrush(RGB(225, 40, 40));
static HBRUSH greenBrush = CreateSolidBrush(RGB(40, 225, 40));
static HBRUSH yellowBrush = CreateSolidBrush(RGB(255, 255, 0));
static HBRUSH greyBrush = CreateSolidBrush(RGB(160, 160, 160));
paintString(hdc, L"Walls: Black ", 3, 3);
paintString(hdc, L"Start Pos: Green ", 113, 3);
paintString(hdc, L"End Pos: Red ", 253, 3);
paintString(hdc, L"Path: Yellow Dots ", 3, 22);
paintString(hdc, L"Closed Nodes: Black Dots ", 203, 22);
paintString(hdc, L"Toggle Wall: Click ", 3, 41);
paintString(hdc, L"Calculate Path: [enter] ", 203, 41);
// draw tiles
for (int x = 0; x < XSIZE; x++)
{
int xStart = OFFSETX + x * TILESIZE;
for (int y = 0; y < YSIZE; y++)
{
auto tile = map[x][y];
RECT location;
location.left = OFFSETX + x * TILESIZE;
location.top = OFFSETY + y * TILESIZE;
location.right = location.left + TILESIZE;
location.bottom = location.top + TILESIZE;
switch (tile)
{
case UNBLOCKING_TILE:
FillRect(hdc, &location, greyBrush);
break;
case BLOCKING_TILE:
FillRect(hdc, &location, blackBrush);
break;
case START_TILE:
FillRect(hdc, &location, greenBrush);
break;
case END_TILE:
FillRect(hdc, &location, redBrush);
break;
default:
break;
}
}
}
// draw path
for (int x = 0; x < XSIZE; x++)
{
int xStart = OFFSETX + x * TILESIZE;
for (int y = 0; y < YSIZE; y++)
{
auto tile = path[x][y];
RECT location;
location.left = OFFSETX + x * TILESIZE;
location.top = OFFSETY + y * TILESIZE;
location.right = location.left + TILESIZE;
location.bottom = location.top + TILESIZE;
location.left += 6;
location.top += 6;
location.right -= 6;
location.bottom -= 6;
if (tile == 1)
{
FillRect(hdc, &location, yellowBrush);
}
else if (tile == 2)
{
FillRect(hdc, &location, blackBrush);
}
}
}
// draw grid overlay
for (int x = 0; x < XSIZE; x++)
{
int xStart = OFFSETX + x * TILESIZE;
for (int y = 0; y < YSIZE; y++)
{
int yStart = OFFSETY + y * TILESIZE;
MoveToEx(hdc, OFFSETX, yStart, NULL);
LineTo(hdc, OFFSETX + XSIZE * TILESIZE, yStart);
}
MoveToEx(hdc, xStart + TILESIZE, OFFSETY, NULL);
LineTo(hdc, xStart + TILESIZE, OFFSETY + YSIZE * TILESIZE);
}
}
LRESULT paintMessageHandler(DummyWindow* dummy, UINT message, WPARAM wparam, LPARAM lparam)
{
RECT rect;
GetClientRect(dummy->getHandle(), &rect);
int width = rect.right - rect.left;
int height = rect.bottom + rect.left;
PAINTSTRUCT ps;
auto hdc = BeginPaint(dummy->getHandle(), &ps);
auto secondhdc = CreateCompatibleDC(hdc);
auto buffer2 = CreateCompatibleBitmap(hdc, width, height);
SelectObject(secondhdc, buffer2);
paintMap(secondhdc);
BitBlt(hdc, 0, 0, width, height, secondhdc, 0, 0, SRCCOPY);
DeleteObject(buffer2);
DeleteDC(secondhdc);
DeleteDC(hdc);
EndPaint(dummy->getHandle(), &ps);
return 0;
}
LRESULT timerMessageHandler(DummyWindow* dummy, UINT message, WPARAM wparam, LPARAM lparam)
{
RedrawWindow(dummy->getHandle(), NULL, NULL, RDW_INVALIDATE);
return 0;
}
LRESULT closeMessageHandler(DummyWindow* dummy, UINT message, WPARAM wparam, LPARAM lparam)
{
DestroyWindow(dummy->getHandle());
return 0;
}
LRESULT destroyMessageHandler(DummyWindow* dummy, UINT message, WPARAM wparam, LPARAM lparam)
{
PostQuitMessage(0);
return 0;
}
LRESULT setCursorHandler(DummyWindow* dummy, UINT message, WPARAM wparam, LPARAM lparam)
{
return DefWindowProc(dummy->getHandle(), message, wparam, lparam);
}
void showWindow()
{
memset((void*)&map, 0, sizeof(map));
memset((void*)&path, 0, sizeof(path));
map[0][10] = START_TILE;
map[19][10] = END_TILE;
dummyWindow = DummyWindowSharedPtr(new DummyWindow(NULL));
dummyWindow->initialize();
DUMMY_SET_MESSAGE_HANDLER(dummyWindow, WM_KEYUP, keyUpHandler);
DUMMY_SET_MESSAGE_HANDLER(dummyWindow, WM_LBUTTONUP, mouseUpHandler);
DUMMY_SET_MESSAGE_HANDLER(dummyWindow, WM_PAINT, paintMessageHandler);
DUMMY_SET_MESSAGE_HANDLER(dummyWindow, WM_TIMER, timerMessageHandler);
DUMMY_SET_MESSAGE_HANDLER(dummyWindow, WM_CLOSE, closeMessageHandler);
DUMMY_SET_MESSAGE_HANDLER(dummyWindow, WM_DESTROY, destroyMessageHandler);
DUMMY_SET_MESSAGE_HANDLER(dummyWindow, WM_SETCURSOR, setCursorHandler);
dummyWindow->doMessageLoop();
}
@@ -0,0 +1,101 @@
#include <stdio.h>
#include <stdint.h>
#include <functional>
#include <vector>
#include <Windows.h>
#include <conio.h>
#include <map>
#include "Game.h"
// this our our state definition
class StateDefinition
{
public:
StateDefinition(){}
~StateDefinition(){}
std::function<bool(GameSensors*)> condition;
std::function<void(GameSensors*, GameActuators*)> reach;
};
// set up the state machine
std::vector<StateDefinition> buildMachine()
{
// build a machine with 10 state definitions
std::vector<StateDefinition> stateMachine(2);
// get the current definition
auto curDef = stateMachine.begin();
// add a state for strong healing
curDef->condition = [](GameSensors* sensors) -> bool {
static float healAt = 50;
if (sensors->detectedStrongHeal())
{
if (sensors->getStrongHealMaxed())
{
healAt -= 1;
}
else
{
auto newHealAt = 100 - sensors->getStrongHealIncrease();
healAt = (healAt + newHealAt) / 2.00f;
}
sensors->clearStrongHealInfo();
}
return sensors->getHealthPercent() > healAt;
};
curDef->reach = [](GameSensors* sensors, GameActuators* actuators) {
actuators->strongHeal();
};
curDef++;
// add a state for weak healing
curDef->condition = [](GameSensors* sensors) -> bool {
static float healAt = 70;
static bool hasLearned = false;
if (!hasLearned && sensors->detectedWeakHeal())
{
hasLearned = false;
healAt = 100 - sensors->getWeakHealIncrease();
}
return sensors->getHealthPercent() > healAt;
};
curDef->reach = [](GameSensors* sensors, GameActuators* actuators) {
actuators->weakHeal();
};
curDef++;
return stateMachine;
}
// do the feedback loop
void doFeedbackLoop(std::vector<StateDefinition> stateMachine)
{
GameSensors sensors;
GameActuators actuators;
while (true)
{
printf("Current Health %d/%d (%0.00f%%)\n", currentHealth, maximumHealth, sensors.getHealthPercent());
for (auto state = stateMachine.begin(); state != stateMachine.end(); state++)
{
if (!state->condition(&sensors))
{
state->reach(&sensors, &actuators);
break;
}
}
getinput(); // since there's no actual game, this just allows you to change the state of the game to see how the feedback loop behaves
Sleep(1000);
}
}
int main (int argc, char *argv[])
{
printf("Type a number to decrease health by that much. EG '2' dreaces health by 2. Press any other key to do nothing.\n");
doFeedbackLoop(buildMachine());
return 0;
}
@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{F28D01D3-BDE9-4992-B3A9-9803D7294F05}</ProjectGuid>
<RootNamespace>Chapter11_StateMachines</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<IntDir>$(SolutionDir)\BuildTemp\$(ProjectName)_$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<IntDir>$(SolutionDir)\BuildTemp\$(ProjectName)_$(Configuration)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="Chapter11_StateMachines.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Game.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Chapter11_StateMachines.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Game.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>
+105
View File
@@ -0,0 +1,105 @@
#pragma once
/*
THIS CODE EXISTS ONLY TO SIMULATE THE PRESENCE OF A GAME,
GAME SENSORS, AND GAME ACTUATORS, AND IS NOT RELEVANT TO THE EXAMPLE CODE
*/
// these variables simulate the memory of the game
int32_t currentHealth = 10;
int32_t maximumHealth = 10;
int32_t lastStrongHealAmount = 0;
bool lastStrongHealFull = false;
bool detectedStrongHeal = false;
int32_t lastWeakHealAmount = 0;
bool detectedWeakHeal = false;
// this emulates our game sensors
class GameSensors
{
public:
GameSensors(){}
~GameSensors(){}
float getHealthPercent()
{
float ch = currentHealth;
float mh = maximumHealth;
return ((ch / mh) * 100.00f);
}
bool detectedStrongHeal()
{
return ::detectedStrongHeal;
}
float getStrongHealIncrease()
{
float ch = lastStrongHealAmount;
float mh = maximumHealth;
return ((ch / mh) * 100.00f);
}
bool getStrongHealMaxed()
{
return lastStrongHealFull;
}
void clearStrongHealInfo()
{
::detectedStrongHeal = false;
}
bool detectedWeakHeal()
{
return ::detectedWeakHeal;
}
float getWeakHealIncrease()
{
float ch = lastWeakHealAmount;
float mh = maximumHealth;
return ((ch / mh) * 100.00f);
}
};
// this emulates our game actuators
class GameActuators
{
public:
GameActuators(){}
~GameActuators(){}
void strongHeal()
{
auto startingHealth = currentHealth;
currentHealth = currentHealth + 4;
currentHealth = currentHealth > maximumHealth ? maximumHealth : currentHealth;
lastStrongHealAmount = currentHealth - startingHealth;
lastStrongHealFull = (currentHealth == maximumHealth);
detectedStrongHeal = true;
printf("Doing strong heal\n");
}
void weakHeal()
{
auto startingHealth = currentHealth;
currentHealth = currentHealth + 2;
currentHealth = currentHealth > maximumHealth ? maximumHealth : currentHealth;
lastWeakHealAmount = currentHealth - startingHealth;
detectedWeakHeal = true;
printf("Doing weak heal\n");
}
};
// since there's no actual game, this jsut allows you to change the state of the game to see how the feedback loop behaves
void getinput()
{
printf("Your selection: ");
auto c = getch();
if (c >= '1' && c <= '9')
currentHealth -= (c - '0');
printf("%c\n", c);
}