Changeset - 57e33102e42b
[Not reviewed]
default
0 1 0
Jason Maltzen - 3 years ago 2021-09-10 00:00:11
jason@hiddenachievement.com
During recipe generation, don't allow min concentration to go be set below 10 or max reagents below 1
1 file changed with 11 insertions and 1 deletions:
0 comments (0 inline, 0 general)
Models/RecipeSearchNode.cs
Show inline comments
 
using System;
 
using System.Collections.Generic;
 
using System.IO;
 
using System.Text.RegularExpressions;
 

	
 
namespace DesertPaintCodex.Models
 
{
 
    public class RecipeSearchNode
 
    {
 
        private readonly uint[] _reagents;
 
        private readonly uint _invalidReagent;
 

	
 
        private int _nextReagentPos;
 
        public int ReagentCount => _nextReagentPos;
 

	
 
        public uint MinConcentration { get; set; } = 10;
 
        private uint _minConcentration = 10;
 
        public uint MinConcentration
 
        {
 
            get => _minConcentration;
 
            set
 
            {
 
                if (value < 10) value = 10;
 
                _minConcentration = value;
 
            }
 
        }
 

	
 
        private readonly bool[] _reagentInUse;
 
        private readonly List<Reagent> _costSortedReagents;
 
        public PaintRecipe? TestRecipe { get; set; }
 

	
 
        public uint CurrentTargetQuantity { get; set; }
 
        public uint MaxConcentration { get; set; }
 
        public uint UsedQuantity { get; private set; }
 
        public uint CatalystCount { get; set; }
 
        public uint FullQuantityDepth { get; set; }
 
        public uint FullQuantity { get; set; }
 
        public uint MinReagents { get; set; }
 

	
 
        public bool ShouldLog { get; private set; }
 

	
 
        public Action<string>? WriteLog = null;
 

	
 
        private uint _maxReagents;
 
        public uint MaxReagents
 
        {
 
            get => _maxReagents;
 
            set
 
            {
 
                if (value < 1) value = 1;
 
                _maxReagents = value;
 
                CurrentWeights = new uint[_maxReagents];
 
            }
 
        }
 

	
 
        public uint[] CurrentWeights { get; private set; }
 
        
 
        public uint LastReagent => _reagents[_nextReagentPos - 1];
 

	
 
        public RecipeSearchNode(RecipeSearchNode other)
 
        {
 
            _costSortedReagents = new List<Reagent>(other._costSortedReagents);
 
            _reagents = new uint[_costSortedReagents.Count];
 
            _invalidReagent = (uint)_costSortedReagents.Count;
 
            for (int i = 0; i < _costSortedReagents.Count; ++i)
 
            {
 
                this._reagents[i] = other._reagents[i];
 
            }
 
            _reagentInUse = new bool[_costSortedReagents.Count];
 
            for (uint i = 0; i < _costSortedReagents.Count; ++i)
 
            {
 
                _reagentInUse[i] = other._reagentInUse[i];
 
            }
 
            _nextReagentPos = other.ReagentCount;
 

	
 
            CurrentTargetQuantity = other.CurrentTargetQuantity;
 
            MaxConcentration = other.MaxConcentration;
 
            UsedQuantity = other.UsedQuantity;
 
            CatalystCount = other.CatalystCount;
 
            FullQuantityDepth = other.FullQuantityDepth;
 
            FullQuantity = other.FullQuantity;
 
            MinReagents = other.MinReagents;
 
            MaxReagents = other.MaxReagents;
 
            CurrentWeights = new uint[MaxReagents];
 
            for (int i = 0; i < MaxReagents; ++i)
 
            {
 
                CurrentWeights[i] = other.CurrentWeights[i];
 
            }
 

	
 
            RefreshShouldLog();
 
        }
 

	
 
        public RecipeSearchNode(List<Reagent> costSortedReagents, uint[] reagents)
 
        {
 
            _costSortedReagents = new List<Reagent>(costSortedReagents);
 
            _reagents = new uint[costSortedReagents.Count];
 
            _invalidReagent = (uint)costSortedReagents.Count;
 
            _nextReagentPos = reagents.Length;
 
            for (int i = _reagents.Length - 1; i >= _reagents.Length; --i)
 
            {
 
                reagents[i] = _invalidReagent;
 
            }
 
            for (int i = reagents.Length - 1; i >= 0; --i)
 
            {
 
                _reagents[i] = reagents[i];
 
                if (reagents[i] == _invalidReagent)
 
                {
 
                    _nextReagentPos = i;
 
                }
 
            }
 
            _reagentInUse = new bool[costSortedReagents.Count];
 
            for (uint reagentIdx = 0; reagentIdx < costSortedReagents.Count; ++reagentIdx)
 
            {
 
                _reagentInUse[reagentIdx] = false;
 
            }
 
            foreach (uint reagentIdx in this._reagents)
 
            {
 
                if (reagentIdx != _invalidReagent)
 
                {
 
                    _reagentInUse[reagentIdx] = true;
 
                }
 
            }
 

	
 
            MinReagents = (uint) _nextReagentPos;
 
            MaxReagents = (uint) _nextReagentPos;
 
            CurrentWeights = new uint[MaxReagents];
 
            UsedQuantity = 0;
 

	
 
            RefreshShouldLog();
 
        }
 

	
 
        // top-level search
 
        public RecipeSearchNode(List<Reagent> costSortedReagents, uint startReagent)
 
        {
 
            _costSortedReagents = new List<Reagent>(costSortedReagents);
 
            _reagents = new uint[costSortedReagents.Count];
 
            _invalidReagent = (uint)costSortedReagents.Count;
 
            _nextReagentPos = 0;
 
            for (int i = 0; i < _reagents.Length; ++i)
 
            {
 
                _reagents[i] = _invalidReagent;
 
            }
 
            _reagentInUse = new bool[costSortedReagents.Count];
 
            for (uint reagentIdx = 0; reagentIdx < costSortedReagents.Count; ++reagentIdx)
 
            {
 
                _reagentInUse[reagentIdx] = false;
 
            }
 
            _reagents[_nextReagentPos++] = NextFreeReagent(startReagent);
 
            //Console.WriteLine("Added reagent {0} at pos {1}", this.reagents[nextReagentPos-1], nextReagentPos-1);
 
            MinReagents = 1; // don't iterate up beyond the start reagent
 
            MaxReagents = 1;
 
            CurrentWeights = new uint[MaxReagents];
 
            UsedQuantity = 0;
 

	
 
            RefreshShouldLog();
 
        }
 

	
 
        public RecipeSearchNode(List<Reagent> costSortedReagents)
 
        {
 
            _costSortedReagents = costSortedReagents;
 
            _reagents = new uint[costSortedReagents.Count];
 
            _invalidReagent = (uint)costSortedReagents.Count;
 
            _nextReagentPos = 0;
 
            for (int i = 0; i < _reagents.Length; ++i)
 
            {
 
                _reagents[i] = _invalidReagent;
 
            }
 
            _reagentInUse = new bool[costSortedReagents.Count];
 
            for (uint reagentIdx = 0; reagentIdx < costSortedReagents.Count; ++reagentIdx)
 
            {
 
                _reagentInUse[reagentIdx] = false;
 
            }
 
            _reagents[_nextReagentPos++] = NextFreeReagent(0);
 
            MinReagents = 0;
 
            MaxReagents = 1;
 
            CurrentWeights = new uint[MaxReagents];
 
            UsedQuantity = 0;
 

	
 
            RefreshShouldLog();
 
        }
 

	
 
        private void RefreshShouldLog()
 
        {
 
            ShouldLog = false;
 
            // (ReagentCount == 5) && (GetReagent(0).Name == "Iron") && (GetReagent(1).Name == "Red Sand") && (GetReagent(2).Name == "Sulfur") && (GetReagent(3).Name == "Carrot") && (GetReagent(4).Name == "Copper");
 
        }
 

	
 
        public Reagent GetReagent(int idx)
 
        {
 
            return _costSortedReagents[(int)_reagents[idx]];
 
        }
 

	
 
        public void RemoveLastReagent()
 
        {
 
            uint reagentIdx = _reagents[_nextReagentPos - 1];
 
            ReleaseReagent(reagentIdx);
 
            if (_costSortedReagents[(int)reagentIdx].IsCatalyst)
 
            {
 
                --CatalystCount;
 
            }
 
            _reagents[_nextReagentPos - 1] = _invalidReagent;
 
            --_nextReagentPos;
 
            RefreshShouldLog();
 
        }
 

	
 
        public void ReplaceLastReagent(uint reagentIdx)
 
        {
 
            uint oldReagentIdx = _reagents[_nextReagentPos - 1];
 
            ReleaseReagent(oldReagentIdx);
 
            _reagents[_nextReagentPos - 1] = reagentIdx;
 
            if (_costSortedReagents[(int)oldReagentIdx].IsCatalyst)
 
            {
 
                --CatalystCount;
 
            }
 
            if (_costSortedReagents[(int)reagentIdx].IsCatalyst)
 
            {
 
                ++CatalystCount;
 
            }
 
            RefreshShouldLog();
 
        }
 

	
 
        public uint NextFreeReagent(uint startIdx)
 
        {
 
            uint idx = startIdx;
 
            for (; idx < _costSortedReagents.Count; ++idx)
 
            {
 
                bool inUse = _reagentInUse[idx];
 
                if ((inUse == false) && (_costSortedReagents[(int)idx].Enabled))
 
                {
 
                    //Console.WriteLine("Found free reagent idx {0}", idx);
 
                    _reagentInUse[idx] = true;
 
                    return idx;
 
                }
 
            }
 
            //Console.WriteLine("Failed to find free reagent.");
 
            return (uint)_costSortedReagents.Count;
 
        }
 

	
 
        private void ReleaseReagent(uint reagentIdx)
 
        {
 
            _reagentInUse[reagentIdx] = false;
 
        }
 

	
 
        public bool AddNextReagent()
 
        {
 
            if (ReagentCount >= MaxReagents) return false;
 

	
 
            uint nextReagent = NextFreeReagent(0);
 
            _reagents[_nextReagentPos++] = nextReagent;
 
            if (_costSortedReagents[(int)nextReagent].IsCatalyst)
 
            {
 
                ++CatalystCount;
 
            }
 
            InitForQuantity(CurrentTargetQuantity);
 

	
 
            return true;
 
        }
 

	
 
        public void InitForQuantity(uint quantity)
 
        {
 
            //System.Console.WriteLine("Init for quantity: {0}, reagent count: {1} ({2} catalysts)", quantity, ReagentCount, CatalystCount);
 
            CurrentTargetQuantity = quantity;
 
            if (CurrentTargetQuantity < (MinConcentration + CatalystCount))
 
            {
 
                // invalid quantity
 
                return;
 
            }
 
            UsedQuantity = 0;
 
            if (ShouldLog)
 
            {
 
                WriteLog?.Invoke($" == initializing {this} @ quantity {CurrentTargetQuantity} with {CatalystCount} catalysts ==");
 
            }
 
            uint remainingReagents = ((uint)_nextReagentPos); // Remaining reagents, including catalysts
 
            uint remainingWeight = CurrentTargetQuantity - CatalystCount;
 
            for (int i = 0; i < _nextReagentPos; ++i)
 
            {
 
                Reagent reagent = GetReagent(i);
 

	
 
                if (reagent.IsCatalyst)
 
                {
 
                    //Console.WriteLine("Init catalyst {0} weight 1", reagent.Name);
 
                    CurrentWeights[i] = 1;
 
                    ++UsedQuantity;
 
                    // This takes quantity but not weight (concentration)
 
                    if (ShouldLog)
 
                    {
 
                        WriteLog?.Invoke($"    + 1 {reagent.Name} (catalyst)");
 
                    }
 
                }
 
                else
 
                {
 
                    uint reagentMaxWeight = reagent.RecipeMax;
 
                    if (ReagentCount <= FullQuantityDepth)
 
                    {
 
                        reagentMaxWeight = Math.Max(FullQuantity, reagentMaxWeight);
 
                    }
 
                    uint weight = Math.Min(remainingWeight - (remainingReagents-1), reagentMaxWeight);
 
                    //Console.WriteLine("Init reagent {0} weight {1}", reagent.Name, weight);
 
                    if (ShouldLog)
 
                    {
 
                        WriteLog?.Invoke($"    + {weight} {reagent.Name} (remain: weight={remainingWeight} reagents={remainingReagents})");
 
                    }
 
                    remainingWeight -= weight;
 
                    CurrentWeights[i] = weight;
 
                    UsedQuantity += weight;
 
                }
 
                --remainingReagents;
 
            }
 
            RefreshShouldLog();
 
        }
 

	
 
        public void SetWeight(int idx, uint quantity)
 
        {
 
            UsedQuantity -= CurrentWeights[idx];
 
            CurrentWeights[idx] = quantity;
 
            UsedQuantity += quantity;
 
        }
 

	
 
        public void SaveState(StreamWriter writer)
 
        {
 
            writer.WriteLine("---SearchNode---");
 
            writer.WriteLine("MinReagents: {0}", MinReagents);
 
            writer.WriteLine("MaxReagents: {0}", MaxReagents);
 
            writer.WriteLine("Reagents: {0}", _nextReagentPos);
 
            for (int i = 0; i < _nextReagentPos; ++i)
 
            {
 
                uint idx = _reagents[i];
 
                uint weight = CurrentWeights[i];
 
                writer.WriteLine("Reagent: {0},{1},{2}", idx, _reagentInUse[idx] ? 1 : 0, weight);
 
            }
 
            // pulled from parent: List<Reagent> costSortedReagents;
 
            // new on construct: PaintRecipe testRecipe = null;
 
            writer.WriteLine("CurrentTargetQuantity: {0}", CurrentTargetQuantity);
 
            writer.WriteLine("MinConcentration: {0}", MinConcentration);
 
            writer.WriteLine("MaxConcentration: {0}", MaxConcentration);
 
            writer.WriteLine("UsedQuantity: {0}", UsedQuantity);
 
            writer.WriteLine("CatalystCount: {0}", CatalystCount);
 
            writer.WriteLine("FullQuantity: {0}", FullQuantity);
 
            writer.WriteLine("FullQuantityDepth: {0}", FullQuantityDepth);
 
            writer.WriteLine("---EndNode---");
 
            
 
        }
 

	
 
        static readonly Regex keyValueRegex = new Regex(@"(\w+)\:\s*(.*)\s*$");
 
        static readonly Regex reagentPartsRegex = new Regex(@"(?<id>\d+),(?<inUse>\d+),(?<weight>\d+)");
 
        public bool LoadState(StreamReader reader)
 
        {
 
            string? line = reader.ReadLine();
 
            
 
            if ((line == null) || !line.Equals("---SearchNode---"))
 
            {
 
                return false;
 
            }
 

	
 
            bool success = true;
 
            while ((line = reader.ReadLine()) != null)
 
            {
 
                if (line.Equals("---EndNode---"))
 
                {
 
                    break;
 
                }
 
                Match match = keyValueRegex.Match(line);
 
                if (match.Success)
 
                {
 
                    switch (match.Groups[1].Value)
 
                    {
 
                        case "Reagents":
 
                            {
 
                                //int reagentCount = int.Parse(match.Groups[2].Value);
 
                                for (int i = 0; i < _reagents.Length; ++i)
 
                                {
 
                                    _reagents[i] = _invalidReagent;
 
                                    _reagentInUse[i] = false;
 
                                }
 
                                _nextReagentPos = 0;
 
                            }
 
                            break;
 
                        case "Reagent":
 
                            {
 
                                Match reagentInfo = reagentPartsRegex.Match(match.Groups[2].Value);
 
                                if (reagentInfo.Success)
 
                                {
 
                                    uint reagentId = uint.Parse(reagentInfo.Groups["id"].Value);
 
                                    int isInUse = int.Parse(reagentInfo.Groups["inUse"].Value);
 
                                    uint weight = uint.Parse(reagentInfo.Groups["weight"].Value);
 
                                    _reagents[_nextReagentPos] = reagentId;
 
                                    CurrentWeights[_nextReagentPos] = weight;
 
                                    if (isInUse != 0)
 
                                    {
 
                                        if (reagentId != _invalidReagent)
 
                                        {
 
                                            _reagentInUse[reagentId] = true;
 
                                        }
 
                                    }
 
                                    ++_nextReagentPos;
 
                                }
 
                                else
 
                                {
 
                                    success = false;
 
                                }
 
                            }
 
                            break;
 
                        case "CurrentTargetQuantity":
 
                            {
 
                                uint value = uint.Parse(match.Groups[2].Value);
 
                                CurrentTargetQuantity = value;
 
                            }
 
                            break;
 
                        case "MaxQuantity":
 
                            {
 
                                uint value = uint.Parse(match.Groups[2].Value);
 
                                MaxConcentration = value;
 
                            }
 
                            break;
 
                        case "MinConcentration":
 
                            {
 
                                uint value = uint.Parse(match.Groups[2].Value);
 
                                MinConcentration = value;
 
                            }
 
                            break;
 
                        case "MaxConcentration":
 
                            {
 
                                uint value = uint.Parse(match.Groups[2].Value);
 
                                MaxConcentration = value;
 
                            }
 
                            break;
 
                        case "MinReagents":
 
                            {
 
                                uint value = uint.Parse(match.Groups[2].Value);
 
                                MinReagents = value;
 
                            }
 
                            break;
 
                        case "MaxReagents":
 
                            {
0 comments (0 inline, 0 general)