Example #1
0
            public override float score()
            {
                // determine how fightable nearby threats are
                var threat = NearbyThreat.greatestThreat(context);

                if (threat == null)
                {
                    return(0);                // no threat means don't score
                }
                // TODO: use better functions to map ratios to score
                // 1. compare core sizes (ratio) -> transform [-40, 40]
                var coreSizeRatio = betterRatio(threat.mind.state.me.core.designMax, context.state.me.core.designMax);
                var coreSizeScore = scoreRatio(coreSizeRatio, 20);

                // 2. compare maneuverability (ratio) -> transform [-20, 20]
                // if we're more maneuverable, it might not be worth fighting
                var maneuverabilityRatio = betterRatio(context.state.me.body.turnPower, threat.mind.state.me.body.turnPower);
                var maneuScore           = scoreRatio(maneuverabilityRatio, 20);

                // 3. compare speed (ratio) -> transform [-40, 40]
                // if we're faster, we want to fight less
                var speedRatio = betterRatio(context.state.me.body.thrustPower, threat.mind.state.me.body.thrustPower);
                var speedScore = scoreRatio(speedRatio, 30);

                // 3. compare energy (ratio) -> transform [-40, 40]
                // if we're faster, we want to fight less
                var energyRatio = betterRatio(threat.mind.state.me.core.energy, context.state.me.core.energy);
                var energyScore = scoreRatio(energyRatio, 30);

                // 4. compare armed state
                var threatWeaponMaxscore = 40;
                var threatWeapon         = threat.mind.entity.GetComponent <Shooter>();
                var myWeapon             = context.state.me.GetComponent <Shooter>();
                // if they're armed, set negative score
                var armoryScore = threatWeapon == null ? 0 : -threatWeaponMaxscore;

                // TODO: compare weapons
                if (myWeapon != null)
                {
                    // if i'm armed too
                    // then negate the score penalty
                    armoryScore += threatWeaponMaxscore;
                }

                // clamp score to [-100, 100] -> transform [0, 1]
                var score = coreSizeScore + maneuScore + speedScore + energyScore + armoryScore;

                context.state.setBoard("judged threat",
                                       new DuckMindState.BoardItem($"E:{energyScore}, C:{coreSizeScore}, M:{maneuScore}, S:{speedScore}",
                                                                   "interaction",
                                                                   Color.Orange, Time.TotalTime + 1f));

                var clampedScore = GMathf.clamp(score, -100, 100);        // clamp score
                var fightable    = GMathf.map01(clampedScore, -100, 100); // map to [-1,1]

                return(fightable);
            }
Example #2
0
        public override void run(params DuckMind[] participants)
        {
            var me   = participants[0];
            var them = participants[1];

            // when a bird is nearby
            // if opinion is negative, then there is cause for fear
            // if opinion is positive, then i feel secure
            // if my anxiety is high, i will react more strongly to both

            // if you get WAY too close, you will be labeled a threat

            var maxOpinionDelta = 4; // range [-4, 4]
            var opinionDelta    = 0;
            var currentOpinion  = me.state.getOpinion(them.state.me);

            if (currentOpinion > Constants.DuckMind.OPINION_ALLY)
            {
                // we're friends, we should have a positive opinion
                // this is based on both anxiety and sociability.
                // TODO: positive opinion from being near friends
                // this should fall off to a negligible account above a certain threshold
            }
            else
            {
                // TODO: take anxiety better into account
                // calculate opinion-affecting factors
                var maxWariness = 10f;
                // [-4, 0]: long-distance wariness
                var longDistanceWariness =
                    (int)TraitCalc.transform(-me.soul.traits.wary, -4, 2, -4, 0);
                // [-4, -1]: close distance wariness
                var closeWariness = 0;
                if (dist < closeDistance)
                {
                    // extreme caution
                    closeWariness =
                        (int)TraitCalc.transform(-me.soul.traits.wary, -4, 0, -4, -1);
                }

                me.state.setBoard("nearby fear",
                                  new DuckMindState.BoardItem($"L: {longDistanceWariness}, C: {closeWariness}", "interaction",
                                                              Color.Orange, Time.TotalTime + 1f));
                var warinessScore = longDistanceWariness + closeWariness;
                opinionDelta += warinessScore;

                // being in the presence of a threat is scary
                me.soul.emotions.spikeFear(Math.Abs(warinessScore / maxWariness));
            }

            // clamp the opinion delta to the required range
            opinionDelta = GMathf.clamp(opinionDelta, -maxOpinionDelta, maxOpinionDelta);
            me.state.addOpinion(them.state.me, GMathf.roundToInt(opinionDelta));
        }
Example #3
0
            public override float score()
            {
                // hunger score is based on the necessity of more energy.
                // let E be energy percentage (energy / max energy), clamp01
                // y = (1 - E)^2
                var energyCore     = context.entity.GetComponent <EnergyCore>();
                var satiation      = 2f; // 200% food
                var ratioSatiation = energyCore.energy / (energyCore.designMax * satiation);
                var invEnergyPerc  = 1 - Mathf.Clamp01(ratioSatiation);

                return(GMathf.pow(invEnergyPerc, 1.4f));
            }
Example #4
0
            public override float score()
            {
                var thresh    = opinionThreshold(context);
                var candidate = bestCandidate(context, thresh);

                if (candidate == null)
                {
                    return(0);
                }
                // scale from 0-100
                return(GMathf.map01clamp01(context.state.getOpinion(candidate.mind.state.me),
                                           thresh, Constants.DuckMind.OPINION_ALLY));
            }
Example #5
0
        public override void run(params DuckMind[] participants)
        {
            if (participants.Length != 2)
            {
                throw new ArgumentException("only two participants", nameof(participants));
            }
            var me       = participants[0]; // this should be "me"
            var myTraits = new Traits(me.soul);
            var giver    = participants[1]; // the one who gave me stuff

            // food value [0, 40]
            var maxFoodValue = 40;
            var foodValue    = (int)(sig.energy / 400f) * maxFoodValue;

            // calculate opinion delta
            var opinionDelta   = 0;
            var currentOpinion = me.state.getOpinion(giver.state.me);

            // calculate receptiveness to food
            // receptive (innate) [0, 1]
            var innateFoodReceiptiveness = TraitCalc.transform(myTraits.receptiveness,
                                                               -0.4f, 1f, 0f, 1f);
            // receptive (happy) [0, 0.5]
            var happyFoodReceptiveness = TraitCalc.transform(me.soul.emotions.happy,
                                                             -1.5f, 1f, 0f, 0.5f);

            // receptive [0, 2]
            var foodReceptiveness = GMathf.clamp(
                innateFoodReceiptiveness + happyFoodReceptiveness,
                0f, 2f
                );

            // significantly diminishing rewards, effectively capping around ~300
            var foodOpinionWeight = LCurves.diminishingReturns(currentOpinion / 10f, 1f, 0.1f);

            opinionDelta += (int)(foodReceptiveness * foodValue * foodOpinionWeight);

            // food makes me happy!
            me.soul.emotions.spikeHappy(GMathf.clamp(foodReceptiveness, 0, 0.8f));

            // add opinion to the one that fed me
            me.state.addOpinion(giver.state.me, opinionDelta);
        }