public override void init()
        {
            strategy = (Strategies)Utils.rnd.Next(4);
            while (strategy == (Strategies)blockedStrategy)
            {
                strategy = (Strategies)Utils.rnd.Next(4);
            }

            payoff       = minInitialPayoff + Utils.rnd.Next(minInitialPayoff);
            prevDistance = 1e9;
            prevGoal     = null;
            maxAge       = maxHumanAge / 2 + Utils.rnd.Next(maxHumanAge / 2);

            punisherGoals    = new List <Human>();
            prevPunisherGoal = null;

            prevHunterGoal     = null;
            prevHunterDistance = 1e6;
            prevMHunters       = -1;

            diedAtStep = -1;

            observedPayoffs = new Hashtable();
        }
        private bool hunterStep()
        {
            bool success = true;
            bool hunted = false;
            base.setImg("caveman");
            List<Entity> nearmammoths = parentEnvironment.getNeighborsOfType(x, y, new Mammoth(parentEnvironment));
            if (nearmammoths.Count > 0 && (nearmammoths.Contains(prevHunterGoal) || prevHunterGoal == null))
            {
                foreach (Mammoth m in nearmammoths) {
                    if (m.isAlive() && (m == prevHunterGoal || prevHunterGoal == null))
                    {
                        List<Entity> nearhumans = parentEnvironment.getDistantNeighborsOfType(m.x, m.y, this);
                        int hunters = 0;
                        int defectors = 0;
                        foreach (Human h in nearhumans)
                        {
                            if (h.strategy != Strategies.Loner) hunters++;
                            if (h.strategy == Strategies.Defector) defectors++;
                        }
                        if (hunters >= humansPerMammoth)
                        {
                            hunted = true;
                            //try to kill mammoth
                            int mpayoff = 0;
                            if (hunters - defectors >= humansPerMammoth)
                            {
                                mpayoff = m.Kill();
                            }
                            foreach (Human h in nearhumans)
                            {
                                if (h.strategy != Strategies.Loner)
                                {
                                    if (mpayoff == 0 && h.src.Contains("hunting")) {
                                        //previous hunt of this mammoth failed
                                        prevHunterGoal = (Mammoth)parentEnvironment.getNearestInLOS(x, y, new Mammoth(parentEnvironment), prevHunterGoal, 2);
                                        if (prevHunterGoal != null) prevHunterDistance = parentEnvironment.getDistance(x, y, prevHunterGoal.x, prevHunterGoal.y);
                                        h.setImg("caveman_punished");
                                    }
                                    h.payoff += (int)(huntPayoffMultiplier * mpayoff / nearhumans.Count);
                                    h.setImg("caveman_hunting");
                                    if (h.strategy != Strategies.Defector)
                                    {
                                        h.payoff -= Utils.rnd.Next(hunterMaxDamage);
                                        if (h.payoff < 0)
                                            h.payoff = 10; //no death, only injury
                                        double p = h.payoff;
                                        if (p > reproductionThreshold) p = reproductionThreshold;
                                        double deathProbability = hunterDeathProbability - (p / reproductionThreshold) * hunterDeathProbability;
                                        if (Utils.rnd.Next(100) < deathProbability * 100)
                                        {
                                            h.setImg("caveman_dead");
                                            h.age = maxAge;

                                            parentEnvironment.lastCounts = ("Died from hunting " + h.strategy.ToString() + "\r\n") + parentEnvironment.lastCounts;
                                        }
                                    }
                                }
                            }
                        }
                        break;
                    }
                }
            }
            if (!hunted)
            {
                int hunters = 0;
                Mammoth mammoth = getTargetMammoth(ref hunters);

                if (prevHunterGoal != null)
                {
                    try
                    {
                        if (parentEnvironment.get(prevHunterGoal.x, prevHunterGoal.y) == null
                        || !(parentEnvironment.get(prevHunterGoal.x, prevHunterGoal.y) is Mammoth)
                        || prevHunterGoal.visible == false || !prevHunterGoal.isAlive())
                        {
                            prevHunterGoal = null;
                            prevHunterDistance = 1e9;
                        }
                    }
                    catch (Exception ex) { }
                }

                if (mammoth != null || prevHunterGoal != null)
                {
                    if (prevHunterGoal == null || mammoth != null && (hunters == -1 || hunters > prevMHunters))
                    {
                        prevHunterGoal = mammoth;
                        prevHunterDistance = parentEnvironment.getDistance(x, y, mammoth.x, mammoth.y);
                        prevMHunters = hunters;
                    }

                    int mindx = 0, mindy = 0;
                    double mindist = 1e9;
                    parentEnvironment.getBestDirection(this, prevHunterGoal, ref mindx, ref mindy, ref mindist);
                    if (mindist <= 2 || parentEnvironment.get(x + mindx * hunterSpeed, y + mindy * hunterSpeed) != null)
                        success = parentEnvironment.move(this, x + mindx, y + mindy);
                    else
                        success = parentEnvironment.move(this, x + mindx * hunterSpeed, y + mindy * hunterSpeed);
                }
            }

            return success;
        }
        public override void init()
        {
            strategy = (Strategies)Utils.rnd.Next(4);
            while (strategy == (Strategies)blockedStrategy) strategy = (Strategies)Utils.rnd.Next(4);

            payoff = minInitialPayoff + Utils.rnd.Next(minInitialPayoff);
            prevDistance = 1e9;
            prevGoal = null;
            maxAge = maxHumanAge / 2 + Utils.rnd.Next(maxHumanAge / 2);

            punisherGoals = new List<Human>();
            prevPunisherGoal = null;

            prevHunterGoal = null;
            prevHunterDistance = 1e6;
            prevMHunters = -1;

            diedAtStep = -1;

            observedPayoffs = new Hashtable();
        }
        public Mammoth getTargetMammoth(ref int prevHunters)
        {
            Entity result = null;
            int i, j;
            int centerX = x, centerY = y;

            //look for signalling hunter
            double distance;
            double mindist = 1e6;
            for (i = 0; i < Environment.envW; i++)
            {
                for (j = 0; j < Environment.envH; j++)
                {
                    if (parentEnvironment.get(i, j) != null && parentEnvironment.get(i, j) is Human && (i != centerX || j != centerY)
                        && parentEnvironment.get(i, j).visible)
                    {
                        double xdist = i - centerX;
                        double ydist = j - centerY;

                        distance = parentEnvironment.getDistance(centerX, centerY, i, j);

                        bool obstructedLOS = parentEnvironment.isObstructedLos(centerX, centerY, i, j);
                        if (!obstructedLOS)
                        {
                            Human h = (Human)parentEnvironment.get(i, j);
                            if (h != null && h.signaling)
                            {
                                if (h.targetMammoth != null && h.targetMammoth.isAlive())
                                {
                                    if (distance < mindist)
                                    {
                                        result = h.targetMammoth;
                                        mindist = distance;
                                    }
                                }
                                else
                                {
                                    h.signaling = false;
                                    h.setImg("caveman");
                                }
                            }
                        }
                    }
                }
            }

            if (result != null) { //signaling human targeting mammoth found
                prevHunters = -1;
                return (Mammoth)result;
            }

            //if no signalling hunter in LOS, get nearest mammoth with most hunters
            prevHunters = -1;
            int hunters;
            double smallestDistance = 1e6;
            for (i = 0; i < Environment.envW; i++)
            {
                for (j = 0; j < Environment.envH; j++)
                {
                    if (parentEnvironment.get(i, j) != null && parentEnvironment.get(i, j) is Mammoth && (i != centerX || j != centerY)
                        && parentEnvironment.get(i, j).visible)
                    {
                        double xdist = i - centerX;
                        double ydist = j - centerY;

                        distance = parentEnvironment.getDistance(centerX, centerY, i, j);

                        bool obstructedLOS = parentEnvironment.isObstructedLos(centerX, centerY, i, j);
                        if (!obstructedLOS)
                        {
                            List<Entity> humans = parentEnvironment.getNeighborsOfType(i, j, this);
                            hunters = 0;
                            foreach (Human h in humans) if (h.strategy != Strategies.Loner) hunters++;
                            if (hunters > prevHunters) {
                                result = parentEnvironment.get(i, j);
                                smallestDistance = distance;
                                prevHunters = hunters;
                            }
                            else if (distance < smallestDistance)
                            {
                                result = parentEnvironment.get(i, j);
                                smallestDistance = distance;
                            }
                        }
                    }
                }
            }

            bool signalingFound = false;
            try
            {
                foreach (int[] entry in observedPayoffs.Values)
                {
                    if (entry[3] == 1) signalingFound = true;
                }
            }
            catch (Exception ex) { }

            if (result != null && !signalingFound && (strategy == Strategies.Cooperator || strategy == Strategies.Punisher))
            {
                targetMammoth = (Mammoth)result;
                signaling = true;
                base.setImg("caveman_signaling");
            }
            else {
                signaling = false;
                base.setImg("caveman");
            }

            return (Mammoth)result;
        }
        private void pnlContainer_MouseUp(object sender, MouseEventArgs e)
        {
            int x = (int)(e.X / (float)pnlContainer.Width * Environment.envW);
            int y = (int)(e.Y / (float)pnlContainer.Height * Environment.envH);

            if (e.Button == MouseButtons.Right)
            {
                environment.remove(x, y);

                txtHumans.Text = environment.count(new Human(environment)).ToString();
                txtMammoths.Text = environment.count(new Mammoth(environment)).ToString();
                txtPlants.Text = environment.count(new Plant(environment)).ToString();
                txtObstacles.Text = environment.count(new Stone(environment)).ToString();

                environment.draw();
            }
            else if (e.Button == MouseButtons.Middle)
            {
                Entity add;
                if (cmbAdd.Text == "Mammoth") add = new Mammoth(environment);
                else if (cmbAdd.Text == "Human") add = new Human(environment);
                else if (cmbAdd.Text == "Plant") add = new Plant(environment);
                else if (cmbAdd.Text == "Stone") add = new Stone(environment);
                else return;

                Entity ent = environment.getNearest(x, y, add);
                if (ent != null)
                    MessageBox.Show(environment.getDistance(x, y, ent.x, ent.y).ToString());
            }
            else {
                Entity ent = environment.get(x, y);
                if (ent != null)
                {
                    txtActions.Text = ("Info:\r\n" + ent.getInfo() + "\r\n(x, y):(" + x + "," + y + ")");
                }
            }
        }
        private void pnlContainer_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            Entity add = null;
            if (cmbAdd.Text == "Mammoth") add = new Mammoth(environment);
            else if (cmbAdd.Text == "Human") add = new Human(environment);
            else if (cmbAdd.Text == "Plant") add = new Plant(environment);
            else if (cmbAdd.Text == "Stone") add = new Stone(environment);

            int x = (int)(e.X / (float)pnlContainer.Width * Environment.envW);
            int y = (int)(e.Y / (float)pnlContainer.Height * Environment.envH);

            if (add != null) add.init();
            if (environment.get(x, y) == null && add != null) environment.add(add, x, y);
            else if (environment.get(x, y) is Human)
            {
                Human h = (Human)environment.get(x, y);
                h.strategy = (Strategies)(((int)h.strategy + 1) % 4);
            }

            txtHumans.Text = environment.count(new Human(environment)).ToString();
            txtMammoths.Text = environment.count(new Mammoth(environment)).ToString();
            txtPlants.Text = environment.count(new Plant(environment)).ToString();
            txtObstacles.Text = environment.count(new Stone(environment)).ToString();

            environment.draw();
        }
        public Mammoth getTargetMammoth(ref int prevHunters)
        {
            Entity result = null;
            int    i, j;
            int    centerX = x, centerY = y;

            //look for signalling hunter
            double distance;
            double mindist = 1e6;

            for (i = 0; i < Environment.envW; i++)
            {
                for (j = 0; j < Environment.envH; j++)
                {
                    if (parentEnvironment.get(i, j) != null && parentEnvironment.get(i, j) is Human && (i != centerX || j != centerY) &&
                        parentEnvironment.get(i, j).visible)
                    {
                        double xdist = i - centerX;
                        double ydist = j - centerY;

                        distance = parentEnvironment.getDistance(centerX, centerY, i, j);

                        bool obstructedLOS = parentEnvironment.isObstructedLos(centerX, centerY, i, j);
                        if (!obstructedLOS)
                        {
                            Human h = (Human)parentEnvironment.get(i, j);
                            if (h != null && h.signaling)
                            {
                                if (h.targetMammoth != null && h.targetMammoth.isAlive())
                                {
                                    if (distance < mindist)
                                    {
                                        result  = h.targetMammoth;
                                        mindist = distance;
                                    }
                                }
                                else
                                {
                                    h.signaling = false;
                                    h.setImg("caveman");
                                }
                            }
                        }
                    }
                }
            }

            if (result != null)   //signaling human targeting mammoth found
            {
                prevHunters = -1;
                return((Mammoth)result);
            }

            //if no signalling hunter in LOS, get nearest mammoth with most hunters
            prevHunters = -1;
            int    hunters;
            double smallestDistance = 1e6;

            for (i = 0; i < Environment.envW; i++)
            {
                for (j = 0; j < Environment.envH; j++)
                {
                    if (parentEnvironment.get(i, j) != null && parentEnvironment.get(i, j) is Mammoth && (i != centerX || j != centerY) &&
                        parentEnvironment.get(i, j).visible)
                    {
                        double xdist = i - centerX;
                        double ydist = j - centerY;

                        distance = parentEnvironment.getDistance(centerX, centerY, i, j);

                        bool obstructedLOS = parentEnvironment.isObstructedLos(centerX, centerY, i, j);
                        if (!obstructedLOS)
                        {
                            List <Entity> humans = parentEnvironment.getNeighborsOfType(i, j, this);
                            hunters = 0;
                            foreach (Human h in humans)
                            {
                                if (h.strategy != Strategies.Loner)
                                {
                                    hunters++;
                                }
                            }
                            if (hunters > prevHunters)
                            {
                                result           = parentEnvironment.get(i, j);
                                smallestDistance = distance;
                                prevHunters      = hunters;
                            }
                            else if (distance < smallestDistance)
                            {
                                result           = parentEnvironment.get(i, j);
                                smallestDistance = distance;
                            }
                        }
                    }
                }
            }


            bool signalingFound = false;

            try
            {
                foreach (int[] entry in observedPayoffs.Values)
                {
                    if (entry[3] == 1)
                    {
                        signalingFound = true;
                    }
                }
            }
            catch (Exception ex) { }

            if (result != null && !signalingFound && (strategy == Strategies.Cooperator || strategy == Strategies.Punisher))
            {
                targetMammoth = (Mammoth)result;
                signaling     = true;
                base.setImg("caveman_signaling");
            }
            else
            {
                signaling = false;
                base.setImg("caveman");
            }

            return((Mammoth)result);
        }
        private bool hunterStep()
        {
            bool success = true;
            bool hunted  = false;

            base.setImg("caveman");
            List <Entity> nearmammoths = parentEnvironment.getNeighborsOfType(x, y, new Mammoth(parentEnvironment));

            if (nearmammoths.Count > 0 && (nearmammoths.Contains(prevHunterGoal) || prevHunterGoal == null))
            {
                foreach (Mammoth m in nearmammoths)
                {
                    if (m.isAlive() && (m == prevHunterGoal || prevHunterGoal == null))
                    {
                        List <Entity> nearhumans = parentEnvironment.getDistantNeighborsOfType(m.x, m.y, this);
                        int           hunters    = 0;
                        int           defectors  = 0;
                        foreach (Human h in nearhumans)
                        {
                            if (h.strategy != Strategies.Loner)
                            {
                                hunters++;
                            }
                            if (h.strategy == Strategies.Defector)
                            {
                                defectors++;
                            }
                        }
                        if (hunters >= humansPerMammoth)
                        {
                            hunted = true;
                            //try to kill mammoth
                            int mpayoff = 0;
                            if (hunters - defectors >= humansPerMammoth)
                            {
                                mpayoff = m.Kill();
                            }
                            foreach (Human h in nearhumans)
                            {
                                if (h.strategy != Strategies.Loner)
                                {
                                    if (mpayoff == 0 && h.src.Contains("hunting"))
                                    {
                                        //previous hunt of this mammoth failed
                                        prevHunterGoal = (Mammoth)parentEnvironment.getNearestInLOS(x, y, new Mammoth(parentEnvironment), prevHunterGoal, 2);
                                        if (prevHunterGoal != null)
                                        {
                                            prevHunterDistance = parentEnvironment.getDistance(x, y, prevHunterGoal.x, prevHunterGoal.y);
                                        }
                                        h.setImg("caveman_punished");
                                    }
                                    h.payoff += (int)(huntPayoffMultiplier * mpayoff / nearhumans.Count);
                                    h.setImg("caveman_hunting");
                                    if (h.strategy != Strategies.Defector)
                                    {
                                        h.payoff -= Utils.rnd.Next(hunterMaxDamage);
                                        if (h.payoff < 0)
                                        {
                                            h.payoff = 10; //no death, only injury
                                        }
                                        double p = h.payoff;
                                        if (p > reproductionThreshold)
                                        {
                                            p = reproductionThreshold;
                                        }
                                        double deathProbability = hunterDeathProbability - (p / reproductionThreshold) * hunterDeathProbability;
                                        if (Utils.rnd.Next(100) < deathProbability * 100)
                                        {
                                            h.setImg("caveman_dead");
                                            h.age = maxAge;

                                            parentEnvironment.lastCounts = ("Died from hunting " + h.strategy.ToString() + "\r\n") + parentEnvironment.lastCounts;
                                        }
                                    }
                                }
                            }
                        }
                        break;
                    }
                }
            }
            if (!hunted)
            {
                int     hunters = 0;
                Mammoth mammoth = getTargetMammoth(ref hunters);


                if (prevHunterGoal != null)
                {
                    try
                    {
                        if (parentEnvironment.get(prevHunterGoal.x, prevHunterGoal.y) == null ||
                            !(parentEnvironment.get(prevHunterGoal.x, prevHunterGoal.y) is Mammoth) ||
                            prevHunterGoal.visible == false || !prevHunterGoal.isAlive())
                        {
                            prevHunterGoal     = null;
                            prevHunterDistance = 1e9;
                        }
                    }
                    catch (Exception ex) { }
                }

                if (mammoth != null || prevHunterGoal != null)
                {
                    if (prevHunterGoal == null || mammoth != null && (hunters == -1 || hunters > prevMHunters))
                    {
                        prevHunterGoal     = mammoth;
                        prevHunterDistance = parentEnvironment.getDistance(x, y, mammoth.x, mammoth.y);
                        prevMHunters       = hunters;
                    }

                    int    mindx = 0, mindy = 0;
                    double mindist = 1e9;
                    parentEnvironment.getBestDirection(this, prevHunterGoal, ref mindx, ref mindy, ref mindist);
                    if (mindist <= 2 || parentEnvironment.get(x + mindx * hunterSpeed, y + mindy * hunterSpeed) != null)
                    {
                        success = parentEnvironment.move(this, x + mindx, y + mindy);
                    }
                    else
                    {
                        success = parentEnvironment.move(this, x + mindx * hunterSpeed, y + mindy * hunterSpeed);
                    }
                }
            }

            return(success);
        }