Exploring raycasters and possibly make a little "doom like" game based on it.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
raycaster/goap.cpp

133 lines
3.7 KiB

#include "dbc.hpp"
#include "goap.hpp"
#include "ai_debug.hpp"
namespace ai {
using namespace nlohmann;
using namespace dbc;
bool is_subset(State& source, State& target) {
State result = source & target;
return result == target;
}
void Action::needs(int name, bool val) {
if(val) {
$positive_preconds[name] = true;
$negative_preconds[name] = false;
} else {
$negative_preconds[name] = true;
$positive_preconds[name] = false;
}
}
void Action::effect(int name, bool val) {
if(val) {
$positive_effects[name] = true;
$negative_effects[name] = false;
} else {
$negative_effects[name] = true;
$positive_effects[name] = false;
}
}
void Action::ignore(int name) {
$positive_preconds[name] = false;
$negative_preconds[name] = false;
}
bool Action::can_effect(State& state) {
return ((state & $positive_preconds) == $positive_preconds) &&
((state & $negative_preconds) == ALL_ZERO);
}
State Action::apply_effect(State& state) {
return (state | $positive_effects) & ~$negative_effects;
}
int distance_to_goal(State from, State to) {
auto result = from ^ to;
return result.count();
}
Script reconstruct_path(std::unordered_map<Action, Action>& came_from, Action& current) {
Script total_path{current};
while(came_from.contains(current)) {
current = came_from.at(current);
if(current != FINAL_ACTION) {
total_path.push_front(current);
}
}
return total_path;
}
inline int h(State start, State goal, Action& action) {
(void)action; // not sure if cost goes here or on d()
return distance_to_goal(start, goal);
}
inline int d(State start, State goal, Action& action) {
return distance_to_goal(start, goal) + action.cost;
}
ActionState find_lowest(std::unordered_map<ActionState, int>& open_set) {
check(!open_set.empty(), "open set can't be empty in find_lowest");
const ActionState *result = nullptr;
int lowest_score = SCORE_MAX;
for(auto& kv : open_set) {
if(kv.second < lowest_score) {
lowest_score = kv.second;
result = &kv.first;
}
}
return *result;
}
ActionPlan plan_actions(std::vector<Action>& actions, State start, State goal) {
std::unordered_map<ActionState, int> open_set;
std::unordered_map<Action, Action> came_from;
std::unordered_map<State, int> g_score;
ActionState current{FINAL_ACTION, start};
g_score[start] = 0;
open_set[current] = g_score[start] + h(start, goal, current.action);
while(!open_set.empty()) {
current = find_lowest(open_set);
if(is_subset(current.state, goal)) {
return {true,
reconstruct_path(came_from, current.action)};
}
open_set.erase(current);
for(auto& neighbor_action : actions) {
// calculate the State being current/neighbor
if(!neighbor_action.can_effect(current.state)) {
continue;
}
auto neighbor = neighbor_action.apply_effect(current.state);
int d_score = d(current.state, neighbor, current.action);
int tentative_g_score = g_score[current.state] + d_score;
int neighbor_g_score = g_score.contains(neighbor) ? g_score[neighbor] : SCORE_MAX;
if(tentative_g_score < neighbor_g_score) {
came_from.insert_or_assign(neighbor_action, current.action);
g_score[neighbor] = tentative_g_score;
// open_set gets the fScore
ActionState neighbor_as{neighbor_action, neighbor};
open_set[neighbor_as] = tentative_g_score + h(neighbor, goal, neighbor_as.action);
}
}
}
return {false, reconstruct_path(came_from, current.action)};
}
}