"Duck vs Dog Riddler, pond.h" - Views: 540 · Hits: 540 - Type: Public

// Pond.h
// somedisagree.com, Jon Wiesman

#pragma once
#include <math.h>

#define PI 3.1415926535897932384626433832
#define TAU (PI * 2)

enum EDuck {
    DumbDuck,
    CowardlyDuck,
    CleverDuck,
    SmartDuck,
};

struct DPoint
{
    double  x;
    double  y;

    void   Set(double _x, double _y) {x = _x; y = _y;}
    double Magnitude() const
    {
        return sqrt(x * x + y * y);
    }
};
static DPoint operator -(const DPoint &l, const DPoint &r)
{
    DPoint pt;
    pt.x = l.x - r.x;
    pt.y = l.y - r.y;
    return pt;
}
static DPoint operator +(const DPoint &l, const DPoint &r)
{
    DPoint pt;
    pt.x = l.x + r.x;
    pt.y = l.y + r.y;
    return pt;
}
static DPoint operator *(const DPoint &l, const DPoint &r)
{
    DPoint pt;
    pt.x = l.x * r.x;
    pt.y = l.y * r.y;
    return pt;
}
static DPoint operator *(const DPoint &l, double scalar)
{
    DPoint pt;
    pt.x = l.x * scalar;
    pt.y = l.y * scalar;
    return pt;
}

class Pond;

class DuckStrategy
{
public:
    virtual DPoint  GetDestinationVector(Pond *pond) = 0;
};

class DumbDuckStrategy : public DuckStrategy
{
public:
    virtual DPoint  GetDestinationVector(Pond *)
    {
        DPoint pt;
        pt.x = -1.0;
        pt.y = 0;
        return pt;
    }
};

class CowardlyDuckStrategy : public DuckStrategy
{
public:
    virtual DPoint  GetDestinationVector(Pond *pond);
};

class CleverDuckStrategy : public CowardlyDuckStrategy
{
public:
    virtual DPoint  GetDestinationVector(Pond *pond);
};

class SmartDuckStrategy : public CowardlyDuckStrategy
{
public:
    virtual DPoint  GetDestinationVector(Pond *pond);
};

class Pond
{
    double  m_dogPos;   // in radians, 0-tau
    double  m_dogSpeed; // in radians/sec
    DPoint  m_duckPos;    
    DPoint  m_lastDuckVector;
    EDuck   m_behavior;
    bool    m_escaped;
    bool    m_beeline;
    double  m_elapsed;
    double  m_beelineTime;

    CowardlyDuckStrategy    m_cowardly;
    DuckStrategy    *m_strategy;

public:
    Pond() : m_dogPos(0)
    {
        SetStrategy(&m_cowardly);
        Restart(PI);
        m_escaped = true;   // so we have to hit restart to start
    }

    void    Restart(double dogSpeed)
    {
        m_beeline = false;
        m_elapsed = 0;
        m_beelineTime = 0;
        m_dogPos = 0;
        m_dogSpeed = dogSpeed;
        m_escaped = false;
        m_duckPos.Set(0, 0);
        m_lastDuckVector.Set(0, 0);
    }

    void    SetStrategy(DuckStrategy *strat)
    {
        m_strategy = strat;
    }

    void    SetBeeline() {m_beeline = true; m_beelineTime = m_elapsed;}
    bool    GetBeeline() const {return m_beeline;}
    double  GetElapsed() const {return m_elapsed;}
    DPoint  GetDuckPos() const {return m_duckPos;}
    double  GetDogPolar() const {return m_dogPos;}
    double  GetDogSpeed() const {return m_dogSpeed;}
    DPoint  GetLastDuckVector() const {return m_lastDuckVector;}
    DPoint  GetDogPos() const 
    {
        DPoint pos;
        pos.x = cos(m_dogPos);
        pos.y = sin(m_dogPos);
        return pos;
    }
    double  GetDogTimeToDest(double dest) const;
    double  GetDuckTimeToDest(double dest) const;

    void    Process(double sec)
    {
        if(m_escaped || m_strategy == NULL)
            return;


        // get duck vector
        DPoint duckVec = m_strategy->GetDestinationVector(this);
        m_lastDuckVector = duckVec;

        // get dog destination
        double mag = m_duckPos.Magnitude();
        double dogDest = PI;
        if(mag != 0.0)
        {
            double s = m_duckPos.y / mag;
            double c = m_duckPos.x / mag;
            dogDest = atan2(s, c);
        }

        // update duck position
        duckVec = duckVec * sec;
        m_duckPos = duckVec + m_duckPos;
        mag = m_duckPos.Magnitude();
        if(mag >= 1.0)
        {
            double overage = mag - 1.0;
            m_duckPos = m_duckPos * (1 / mag);
            sec -= overage;
            m_escaped = true;
        }
        m_elapsed += sec;
        if(mag < sec)
        {
            m_duckPos.Set(0, 0);
        }
        

        // update dog position
        double dogDelta = dogDest - m_dogPos;
        if(dogDelta < -TAU)
            dogDelta += TAU;
        else if(dogDelta > TAU)
            dogDelta -= TAU;

        double dogProgress = m_dogSpeed * sec;
        if(dogProgress > abs(dogDelta))
        {
            m_dogPos = dogDest;
        }
        else
        {
            m_dogPos += dogProgress;
            if(m_dogPos > PI)
                m_dogPos -= TAU;
            else if(m_dogPos < -PI)
                m_dogPos += TAU;
        }
    }
};