Files @ 4778889395e8
Branch filter:

Location: ATITD-Tools/Desert-Paint-Codex/Services/ReagentService.cs - annotation

Jason Maltzen
Change the displayed permeutation count to display using locale-specific number formatting (with comma / dot separators). Fix a crash when starting a new round of recipe generation after recipe generation completed during a previous run.
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
6919080271d5
6919080271d5
40eaee10ae56
40eaee10ae56
6919080271d5
6919080271d5
40eaee10ae56
40eaee10ae56
6919080271d5
6919080271d5
6919080271d5
6919080271d5
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
6919080271d5
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
6919080271d5
40eaee10ae56
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
40eaee10ae56
40eaee10ae56
40eaee10ae56
6919080271d5
40eaee10ae56
40eaee10ae56
6919080271d5
40eaee10ae56
40eaee10ae56
6919080271d5
6919080271d5
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
6919080271d5
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
6919080271d5
6919080271d5
40eaee10ae56
40eaee10ae56
6919080271d5
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
6919080271d5
40eaee10ae56
40eaee10ae56
6919080271d5
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
6919080271d5
40eaee10ae56
40eaee10ae56
40eaee10ae56
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
6919080271d5
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
40eaee10ae56
6919080271d5
6919080271d5
40eaee10ae56
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
6919080271d5
40eaee10ae56
40eaee10ae56
using System;
using System.IO;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text.RegularExpressions;
using DesertPaintCodex.Models;
using DesertPaintCodex.Util;

namespace DesertPaintCodex.Services
{
    internal static class ReagentService
    {
        private static readonly Regex _ppReagentRegex        = new(@"(?<name>\w+)\s*\|\s*(?<red>\d+),\s*(?<green>\d+),\s*(?<blue>\d+)\s*\|\s*(?<cost>\d+)\s*\|\s*(?<enabled>[YN])\s*\|\s*(?<bulk>(bulk|normal))\s*\|\s*(?<max>\d+).*");
        private static readonly Regex _ppCatalystRegex       = new(@"(?<name>(\w*\s)*\w+\w+)\s*\|\s*catalyst\s*\|\s*(?<cost>\d+)\s*\|\s*(?<enabled>[YN])\s*\|\s*(?<bulk>(bulk|normal)).*");
        private static readonly Regex _internalReagentRegex  = new(@"(?<name>(\w*\s)*\w+)\s*\|\s*(?<ppname>\w+)\s*\|\s*(?<red>\d+),\s*(?<green>\d+),\s*(?<blue>\d+).*");
        private static readonly Regex _internalCatalystRegex = new(@"(?<name>(\w+\s)*\w+)\s*\|\s*(?<ppname>\w+)\s*\|\s*catalyst.*");
        private static readonly Regex _renameHeader          = new(@"#\s*(?<header>\w*)");
        private static readonly Regex _renameRegex           = new(@"(?<oldName>(\w+\s)*\w+)\s*\|\s*(?<newName>(\w+\s)*\w+)");

        private static readonly Dictionary<string, Reagent> _reagents   = new();
        private static readonly Dictionary<string, string>  _ppToInternalName = new(); // pp name to our name
        private static readonly Dictionary<string, string> _ppRenames = new();
        private static readonly Dictionary<string, string> _internalRenames = new();
        
        private static bool _initialized = false;

        public static List<string> Names { get; } = new();


        private static void Initialize()
        {
            if (_initialized) return;

            string? reagentsPath = FileUtils.FindApplicationResourceFile("ingredients.txt");
            Debug.Assert(reagentsPath != null);

            Load(reagentsPath);

            string? renamesPath = FileUtils.FindApplicationResourceFile("renames.txt");

            if (renamesPath != null)
            {
                LoadAliases(renamesPath);
            }

            _initialized = true;
        }
        
        // Loads reagent name/colors
        private static void Load(string file)
        {
            _reagents.Clear();
            _ppToInternalName.Clear();
            Names.Clear();

            int index = 0;

            using StreamReader reader = new(file);
            string? line;
            while ((line = reader.ReadLine()) != null)
            {
                Match match = _internalReagentRegex.Match(line);
                if (match.Success)
                {
                    string name   = match.Groups["name"].Value;
                    string ppname = match.Groups["ppname"].Value;
                    Debug.WriteLine("Adding reagent[" + name + "] from line: [" + line + "]");
                    _reagents.Add(name,
                        new Reagent(name, ppname,
                            byte.Parse(match.Groups["red"].Value),
                            byte.Parse(match.Groups["green"].Value),
                            byte.Parse(match.Groups["blue"].Value),
                            index++));
                    // nameStore.AppendValues(name);
                    Names.Add(name);
                    _ppToInternalName.Add(ppname, name);
                }
                else
                {
                    match = _internalCatalystRegex.Match(line);
                    
                    if (!match.Success) continue;
                    
                    string name   = match.Groups["name"].Value;
                    string ppname = match.Groups["ppname"].Value;
                    _reagents.Add(name, new Reagent(ppname, ppname, index++));
                    // nameStore.AppendValues(name);
                    Names.Add(name);
                    _ppToInternalName.Add(ppname, name);
                }
            }
        }

        private static void LoadAliases(string file)
        {
            _ppRenames.Clear();
            _internalRenames.Clear();

            Dictionary<string, string>? renameDict = null;
            
            using StreamReader reader = new(file);
            string? line;
            while ((line = reader.ReadLine()) != null)
            {
                Match match = _renameHeader.Match(line);
                if (match.Success)
                {
                    string header = match.Groups["header"].Value;
                    renameDict = header switch
                    {
                        "PracticalPaint" => _ppRenames,
                        "Internal"       => _internalRenames,
                        _                => renameDict
                    };
                }

                if (renameDict == null) continue;
                
                match = _renameRegex.Match(line);
                
                if (!match.Success) continue;
                
                string oldName = match.Groups["oldName"].Value;
                string newName = match.Groups["newName"].Value;
                renameDict.Add(oldName, newName);
            }
        }

        public static void LoadProfileReagents(string file)
        {
            Initialize();
            
            using StreamReader reader = new(file);
            string? line;
            while ((line = reader.ReadLine()) != null)
            {
                Match match = _ppReagentRegex.Match(line);
                if (match.Success)
                {
                    string ppname = match.Groups["name"].Value;
                    if (_ppRenames.TryGetValue(ppname, out string? rename))
                    {
                        ppname = rename; // Apply rename.
                    }
                    
                    if (_ppToInternalName.TryGetValue(ppname, out string? name))
                    {
                        Reagent reagent = GetReagent(name);
                        if (reagent.IsCatalyst) continue;
                        reagent.Enabled   = match.Groups["enabled"].Value.Equals("Y");
                        reagent.Cost      = uint.Parse(match.Groups["cost"].Value);
                        reagent.RecipeMax = uint.Parse(match.Groups["max"].Value);
                    }
                }
                else
                {
                    match = _ppCatalystRegex.Match(line);
                    
                    if (!match.Success) continue;
                    
                    string ppname = match.Groups["name"].Value;
                    if (_ppRenames.TryGetValue(ppname, out string? rename))
                    {
                        ppname = rename; // Apply rename.
                    }
                    
                    if (_ppToInternalName.TryGetValue(ppname, out string? name))
                    {
                        Reagent reagent = GetReagent(name);
                        
                        if (reagent is not {IsCatalyst: true}) continue;
                        
                        reagent.Enabled = match.Groups["enabled"].Value.Equals("Y");
                        reagent.Cost    = uint.Parse(match.Groups["cost"].Value);
                    }
                }
            }
        }

        public static void SaveProfileReagents(string file)
        {
            Initialize();
            
            using StreamWriter writer = new(file);
            writer.WriteLine("// Ingredients are in the form:");
            writer.WriteLine("// Name | RGB values | cost | enabled (Y/N) | bulk/normal | max items per paint (1-20)");
            writer.WriteLine("//");
            writer.WriteLine("// It is recommended to only change the cost value");
            writer.WriteLine("// It is not recommended to set many of the ingredients above 10 per paint");

            List<Reagent> sortedReagents = new(_reagents.Count);
            sortedReagents.AddRange(_reagents.Values);
            sortedReagents.Sort((x, y) =>
                ((x.IsCatalyst && !y.IsCatalyst) ?
                    1 : ((y.IsCatalyst && !x.IsCatalyst) ?
                        -1 : string.Compare(x.PracticalPaintName, y.PracticalPaintName, StringComparison.InvariantCulture))));
            
            foreach (Reagent reagent in sortedReagents)
            {
                if (!reagent.IsCatalyst)
                {
                    writer.WriteLine("{0,-10} | {1,3}, {2,3}, {3,3} | {4,7} | {5} | {6} | {7}",
                        reagent.PracticalPaintName,
                        reagent.Color?.Red, reagent.Color?.Blue, reagent.Color?.Green,
                        reagent.Cost,
                        reagent.Enabled ? "Y" : "N",
                        reagent.RecipeMax >= 10 ? "  bulk" : "normal",
                        reagent.RecipeMax);
                }
                else
                {
                    writer.WriteLine("{0,-10} | catalyst | {1,7} | {2} | normal | 1",
                        reagent.PracticalPaintName,
                        reagent.Cost,
                        reagent.Enabled ? "Y" : "N");
                }
            }
        }


        public static void InitializeReactions(ReactionSet reactions)
        {
            Initialize();
            foreach (KeyValuePair<string, Reagent> pair1 in _reagents)
            {
                foreach (KeyValuePair<string, Reagent> pair2 in _reagents)
                {
                    if (pair1.Key != pair2.Key)
                    {
                        reactions.Set(pair1.Value, pair2.Value, null);
                    }
                }
            }
        }

        public static Reagent GetReagent(string reagentName)
        {
            Initialize();

            Reagent? returnVal = GetReagentByInternalName(reagentName);

            if (returnVal != null) return returnVal;

            returnVal = GetReagentByPPName(reagentName);

            if (returnVal != null) return returnVal;
            
            string? rename = null;
            if (_internalRenames.TryGetValue(reagentName, out rename))
            {
                returnVal = GetReagentByInternalName(rename);
                if (returnVal != null) return returnVal;
            }

            if (_ppRenames.TryGetValue(reagentName, out rename))
            {
                returnVal = GetReagentByPPName(rename);
                if (returnVal != null) return returnVal;
            }
            
            Debug.Assert(returnVal != null);

            return returnVal;
        }

        private static Reagent? GetReagentByInternalName(string internalName)
        {
            return _reagents.TryGetValue(internalName, out Reagent? returnVal) ? returnVal : null;
        }

        private static Reagent? GetReagentByPPName(string ppName)
        {
            if (!_ppToInternalName.TryGetValue(ppName, out string? otherName)) return null;
            
            return _reagents.TryGetValue(otherName, out Reagent? returnVal) ? returnVal : null;
        }
    }
}