();
+
+ // Match: comma, capture and match anything between
+ string regexPattern = @"(?:,|^)\(([&|](?:[^()]|(?\()|(?<-p>\)))+)\)(?:,|$)|,";
+ string[] subLogic = Regex.Split(logic, regexPattern);
+ foreach (string sequence in subLogic)
+ {
+ if (string.IsNullOrEmpty(sequence))
+ {
+ continue;
+ }
+
+ switch(sequence[0])
+ {
+ // If the first character of the string is & or |, it's a compound dependency
+ case '&':
+ AndDependency andDependency = new AndDependency(sequence.Substring(1));
+ dependencies.Add(andDependency);
+ break;
+ case '|':
+ OrDependency orDependency = new OrDependency(sequence.Substring(1));
+ dependencies.Add(orDependency);
+ break;
+ default:
+
+ break;
+ }
+ }
+
+ return dependencies;
+ }
+
+ private Item RequiredItem;
+
+ public Dependency()
+ {
+
+ }
+
+ public Dependency(string dependencyText)
+ {
+ string[] dependencyParts = dependencyText.Split('.');
+ switch(dependencyParts[0].ToLower())
+ {
+ case "locations":
+
+ break;
+ case "items":
+ if (Enum.TryParse(dependencyParts[1], out ItemType type))
+ {
+ byte subType = 0;
+ if (dependencyParts.Length >= 2)
+ {
+ if (!byte.TryParse(dependencyParts[2], NumberStyles.HexNumber, null, out subType))
+ {
+ if (Enum.TryParse(dependencyParts[2], out KinstoneType subKinstoneType))
+ {
+ subType = (byte)subKinstoneType;
+ }
+ }
+ }
+ RequiredItem = new Item(type, subType);
+ }
+ else
+ {
+ RequiredItem = new Item(ItemType.Untyped, 0);
+ }
+ break;
+ }
+ }
+
+ public virtual bool DependencyFulfilled(List- unplacedItems)
+ {
+ return false;
+ }
+
+ }
+
+ public class AndDependency : Dependency
+ {
+ public List AndList;
+
+ public AndDependency(string dependencyText)
+ {
+ AndList = GetDependencies(dependencyText);
+ }
+
+ override public bool DependencyFulfilled(List
- unplacedItems)
+ {
+ foreach (Dependency dependency in AndList)
+ {
+ if (dependency.DependencyFulfilled(unplacedItems) == false)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ public class OrDependency : Dependency
+ {
+ public List OrList;
+
+ public OrDependency(string dependencyText)
+ {
+ OrList = GetDependencies(dependencyText);
+ }
+
+ override public bool DependencyFulfilled(List
- unplacedItems)
+ {
+ foreach (Dependency dependency in OrList)
+ {
+ if (dependency.DependencyFulfilled(unplacedItems) == true)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+}
diff --git a/Randomizer/Location.cs b/Randomizer/Location.cs
index 756e9c1e..1cddc61f 100644
--- a/Randomizer/Location.cs
+++ b/Randomizer/Location.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
@@ -11,30 +12,118 @@ namespace MinishRandomizer.Randomizer
{
public class Location
{
- public enum Region
+ public static Location GetLocation(string locationText)
{
- Overworld,
- Deepwood,
- Flames,
- WindFort,
- Droplets,
- WindTemple
+ // Location format: Type;Name;Address;Large;Logic
+ string[] locationParts = locationText.Split(';');
+
+ if (locationParts.Length < 4)
+ {
+ return new Location(LocationType.Untyped, "INVALID LOCATION", 0, false, null);
+ }
+
+ string name = locationParts[0];
+
+ string locationType = locationParts[1];
+ if (!Enum.TryParse(locationType, out LocationType type) || type == LocationType.Untyped)
+ {
+ return new Location(LocationType.Untyped, "INVALID LOCATION", 0, false, null);
+ }
+
+ int address = GetAddressFromString(locationParts[2]);
+
+ string largeText = locationParts[3];
+ if (!bool.TryParse(largeText, out bool large))
+ {
+ large = false;
+ }
+
+ string logic = "";
+ if (locationParts.Length >= 5)
+ {
+ logic = locationParts[4];
+ }
+
+ List dependencies = Dependency.GetDependencies(logic);
+
+ return new Location(type, name, address, large, dependencies);
}
- public byte Type;
- public Region SubRegion;
- public bool Large;
- public Item DefaultContents { get; private set; }
- public Item Contents;
- public int Address { get; private set; }
+ public static int GetAddressFromString(string addressString)
+ {
+ // Either direct address or area-room-chest
+ if (int.TryParse(addressString, NumberStyles.HexNumber, null, out int address))
+ {
+ return address;
+ }
+
+ string[] chestDetails = addressString.Split('-');
+ if (chestDetails.Length != 3)
+ {
+ return 0;
+ }
+
+ if (!int.TryParse(chestDetails[0], NumberStyles.HexNumber, null, out int area))
+ {
+ return 0;
+ }
+
+ if (!int.TryParse(chestDetails[1], NumberStyles.HexNumber, null, out int room))
+ {
+ return 0;
+ }
+
+ if (!int.TryParse(chestDetails[2], NumberStyles.HexNumber, null, out int chest))
+ {
+ return 0;
+ }
+
+ int areaTableAddr = ROM.Instance.reader.ReadAddr(ROM.Instance.headers.AreaMetadataBase + (area << 2));
+ int roomTableAddr = ROM.Instance.reader.ReadAddr(areaTableAddr + (room << 2));
+ int chestTableAddr = ROM.Instance.reader.ReadAddr(roomTableAddr + 0x0C);
- public Location(byte type, Region region, int address, bool large, Item contents)
+ return chestTableAddr + chest * 8 + 0x02;
+ }
+
+ public enum LocationType
+ {
+ Untyped,
+ Normal,
+ Minor,
+ DungeonItem,
+ NPCItem,
+ KinstoneItem,
+ HeartPieceItem,
+ HelperItem
+ }
+
+ public List Dependencies;
+ public LocationType Type;
+ public string Name;
+ public bool Filled;
+ public Item Contents { get; private set; }
+ private bool Large;
+ private Item DefaultContents;
+ private int Address;
+
+ public Location(LocationType type, string name, int address, bool large, List dependencies)
{
Type = type;
- DefaultContents = contents;
- Contents = contents;
+ Name = name;
+
Address = address;
+
Large = large;
+
+ Dependencies = dependencies;
+
+ if (address != 0)
+ {
+ DefaultContents = GetItemContents();
+ Contents = DefaultContents;
+ }
+
+ Filled = false;
}
public void WriteLocation(Writer r)
@@ -46,5 +135,36 @@ public void WriteLocation(Writer r)
r.WriteByte(Contents.SubValue);
}
}
+
+ public Item GetItemContents()
+ {
+ ItemType type = (ItemType)ROM.Instance.reader.ReadByte(Address);
+ byte subType = 0;
+ if (Large)
+ {
+ subType = ROM.Instance.reader.ReadByte();
+ }
+
+ return new Item(type, subType);
+ }
+
+ public bool IsAccessible(List
- unplacedItems)
+ {
+ foreach (Dependency dependency in Dependencies)
+ {
+ if (!dependency.DependencyFulfilled(unplacedItems))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public void Fill(Item contents)
+ {
+ Contents = contents;
+ Filled = true;
+ }
}
}
diff --git a/Randomizer/Shuffler.cs b/Randomizer/Shuffler.cs
index 3bbc8dd3..6f021a50 100644
--- a/Randomizer/Shuffler.cs
+++ b/Randomizer/Shuffler.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using MinishRandomizer.Core;
@@ -28,66 +29,98 @@ public Item(ItemType type, byte subValue)
Kinstone = KinstoneType.UnTyped;
}
}
+
+ public override bool Equals(object obj)
+ {
+ if (obj == null || GetType() != obj.GetType())
+ {
+ return false;
+ }
+ Item asItem = (Item)obj;
+ return asItem.Type == Type && asItem.SubValue == SubValue;
+ }
+
+ public override int GetHashCode()
+ {
+ return base.GetHashCode();
+ }
}
public class Shuffler
{
-
- List Locations;
- List
- Items;
- public Shuffler()
+ private Random RNG;
+ private List Locations;
+ private List
- Items;
+ private string OutputDirectory;
+
+ public Shuffler(string outputDirectory)
{
+ RNG = new Random();
+ Locations = new List();
+ Items = new List
- ();
+ OutputDirectory = outputDirectory;
+ }
+ public void SetSeed(int seed)
+ {
+ RNG = new Random(seed);
}
public void LoadLocations(string locationFile)
{
- byte[] locationData;
+ string[] locationStrings;
if (locationFile == null)
{
- locationData = File.ReadAllBytes("");
+ Assembly assembly = Assembly.GetExecutingAssembly();
+ using (Stream stream = assembly.GetManifestResourceStream("MinishRandomizer.Resources.default.logic"))
+ using (StreamReader reader = new StreamReader(stream))
+ {
+ string allLocations = reader.ReadToEnd();
+ locationStrings = allLocations.Split('\n');
+ }
}
else
{
- locationData = File.ReadAllBytes(locationFile);
+ locationStrings = File.ReadAllLines(locationFile);
}
-
- Locations = new List();
- Items = new List
- ();
-
- using (MemoryStream ms = new MemoryStream(locationData))
+
+ foreach (string locationString in locationStrings)
{
- Reader r = new Reader(ms);
-
- byte type = r.ReadByte();
- while (type != 0)
- {
- Location.Region region = (Location.Region)r.ReadByte();
- int addr = r.ReadAddr();
- bool large = r.ReadByte() == 1;
- ItemType itemType = (ItemType)r.ReadByte();
- byte subValue = r.ReadByte();
- Item item = new Item(itemType, subValue);
-
- Locations.Add(new Location(type, region, addr, large, item));
-
- Items.Add(item);
-
- type = r.ReadByte();
- }
+ Location newLocation = Location.GetLocation(locationString);
+ Locations.Add(newLocation);
+ Items.Add(newLocation.Contents);
}
}
- public void LoadLogic(string logicFile)
+ public void RandomizeLocations()
{
+ List unfilledLocations = Locations.Where(location => !location.Filled).ToList();
+ while (unfilledLocations.Count > 0)
+ {
+ Location location = unfilledLocations[RNG.Next(unfilledLocations.Count)];
+ Item item = Items[RNG.Next(Items.Count)];
+ List
- unplacedItems = Items.Where(listItem => !ReferenceEquals(listItem, item)).ToList();
+ if (location.IsAccessible(unplacedItems))
+ {
+ Console.WriteLine($"Loc: {location.Name} Item: {item.Type.ToString()}");
+ Items.Remove(item);
+ location.Fill(item);
+ unfilledLocations.Remove(location);
+ }
+ }
- }
+ using (MemoryStream ms = new MemoryStream(ROM.Instance.romData))
+ {
+ Writer writer = new Writer(ms);
+ foreach (Location location in Locations)
+ {
+ location.WriteLocation(writer);
+ }
+ }
- public void RandomizeLocations()
- {
-
+ File.WriteAllBytes(OutputDirectory + "/mcrando.gba", ROM.Instance.romData);
}
}
}
diff --git a/Resources/default.locdata b/Resources/default.locdata
deleted file mode 100644
index e69de29b..00000000
diff --git a/Resources/default.logic b/Resources/default.logic
new file mode 100644
index 00000000..4bcacd0b
--- /dev/null
+++ b/Resources/default.logic
@@ -0,0 +1,2 @@
+Smith's House;Minor;22-11-00;true
+Shield Location;Normal;0000FA6A;true
\ No newline at end of file
diff --git a/UI/MainWindow.Designer.cs b/UI/MainWindow.Designer.cs
index 40c6a221..25d2e099 100644
--- a/UI/MainWindow.Designer.cs
+++ b/UI/MainWindow.Designer.cs
@@ -29,32 +29,49 @@ protected override void Dispose(bool disposing)
private void InitializeComponent()
{
this.statusStrip1 = new System.Windows.Forms.StatusStrip();
+ this.statusText = new System.Windows.Forms.ToolStripStatusLabel();
this.menuStrip1 = new System.Windows.Forms.MenuStrip();
this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.openToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.randomize = new System.Windows.Forms.Button();
- this.statusText = new System.Windows.Forms.ToolStripStatusLabel();
+ this.logicTab = new System.Windows.Forms.TabPage();
+ this.customLogicCheckBox = new System.Windows.Forms.CheckBox();
+ this.customLocationPath = new System.Windows.Forms.TextBox();
+ this.browseLogicButton = new System.Windows.Forms.Button();
+ this.browseLocationButton = new System.Windows.Forms.Button();
+ this.customLogicPath = new System.Windows.Forms.TextBox();
+ this.customLocationCheckBox = new System.Windows.Forms.CheckBox();
+ this.mainTabs = new System.Windows.Forms.TabControl();
+ this.locationsTab = new System.Windows.Forms.TabPage();
this.statusStrip1.SuspendLayout();
this.menuStrip1.SuspendLayout();
+ this.logicTab.SuspendLayout();
+ this.mainTabs.SuspendLayout();
+ this.locationsTab.SuspendLayout();
this.SuspendLayout();
//
// statusStrip1
//
this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.statusText});
- this.statusStrip1.Location = new System.Drawing.Point(0, 200);
+ this.statusStrip1.Location = new System.Drawing.Point(0, 294);
this.statusStrip1.Name = "statusStrip1";
- this.statusStrip1.Size = new System.Drawing.Size(345, 22);
+ this.statusStrip1.Size = new System.Drawing.Size(390, 22);
this.statusStrip1.TabIndex = 0;
this.statusStrip1.Text = "statusStrip";
//
+ // statusText
+ //
+ this.statusText.Name = "statusText";
+ this.statusText.Size = new System.Drawing.Size(0, 17);
+ //
// menuStrip1
//
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.fileToolStripMenuItem});
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
this.menuStrip1.Name = "menuStrip1";
- this.menuStrip1.Size = new System.Drawing.Size(345, 24);
+ this.menuStrip1.Size = new System.Drawing.Size(390, 24);
this.menuStrip1.TabIndex = 1;
this.menuStrip1.Text = "menuStrip1";
//
@@ -69,13 +86,13 @@ private void InitializeComponent()
// openToolStripMenuItem
//
this.openToolStripMenuItem.Name = "openToolStripMenuItem";
- this.openToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
+ this.openToolStripMenuItem.Size = new System.Drawing.Size(103, 22);
this.openToolStripMenuItem.Text = "Open";
this.openToolStripMenuItem.Click += new System.EventHandler(this.OpenToolStripMenuItem_Click);
//
// randomize
//
- this.randomize.Location = new System.Drawing.Point(258, 27);
+ this.randomize.Location = new System.Drawing.Point(19, 268);
this.randomize.Name = "randomize";
this.randomize.Size = new System.Drawing.Size(75, 23);
this.randomize.TabIndex = 2;
@@ -83,17 +100,108 @@ private void InitializeComponent()
this.randomize.UseVisualStyleBackColor = true;
this.randomize.Click += new System.EventHandler(this.Randomize_Click);
//
- // statusText
+ // logicTab
//
- this.statusText.Name = "statusText";
- this.statusText.Size = new System.Drawing.Size(59, 17);
- this.statusText.Text = "";
+ this.logicTab.Controls.Add(this.customLogicPath);
+ this.logicTab.Controls.Add(this.browseLogicButton);
+ this.logicTab.Controls.Add(this.customLogicCheckBox);
+ this.logicTab.Location = new System.Drawing.Point(4, 22);
+ this.logicTab.Name = "logicTab";
+ this.logicTab.Padding = new System.Windows.Forms.Padding(3);
+ this.logicTab.Size = new System.Drawing.Size(361, 209);
+ this.logicTab.TabIndex = 0;
+ this.logicTab.Text = "Logic";
+ this.logicTab.UseVisualStyleBackColor = true;
+ //
+ // customLogicCheckBox
+ //
+ this.customLogicCheckBox.AutoSize = true;
+ this.customLogicCheckBox.Location = new System.Drawing.Point(5, 152);
+ this.customLogicCheckBox.Name = "customLogicCheckBox";
+ this.customLogicCheckBox.Size = new System.Drawing.Size(112, 17);
+ this.customLogicCheckBox.TabIndex = 5;
+ this.customLogicCheckBox.Text = "Use Custom Logic";
+ this.customLogicCheckBox.UseVisualStyleBackColor = true;
+ this.customLogicCheckBox.CheckedChanged += new System.EventHandler(this.CustomLogicCheckBox_CheckedChanged);
+ //
+ // customLocationPath
+ //
+ this.customLocationPath.Enabled = false;
+ this.customLocationPath.Location = new System.Drawing.Point(113, 175);
+ this.customLocationPath.Name = "customLocationPath";
+ this.customLocationPath.Size = new System.Drawing.Size(206, 20);
+ this.customLocationPath.TabIndex = 3;
+ //
+ // browseLogicButton
+ //
+ this.browseLogicButton.Enabled = false;
+ this.browseLogicButton.Location = new System.Drawing.Point(32, 175);
+ this.browseLogicButton.Name = "browseLogicButton";
+ this.browseLogicButton.Size = new System.Drawing.Size(75, 23);
+ this.browseLogicButton.TabIndex = 7;
+ this.browseLogicButton.Text = "Browse...";
+ this.browseLogicButton.UseVisualStyleBackColor = true;
+ this.browseLogicButton.Click += new System.EventHandler(this.BrowseLogicButton_Click);
+ //
+ // browseLocationButton
+ //
+ this.browseLocationButton.Enabled = false;
+ this.browseLocationButton.Location = new System.Drawing.Point(32, 175);
+ this.browseLocationButton.Name = "browseLocationButton";
+ this.browseLocationButton.Size = new System.Drawing.Size(75, 23);
+ this.browseLocationButton.TabIndex = 4;
+ this.browseLocationButton.Text = "Browse...";
+ this.browseLocationButton.UseVisualStyleBackColor = true;
+ this.browseLocationButton.Click += new System.EventHandler(this.BrowseLocationButton_Click);
+ //
+ // customLogicPath
+ //
+ this.customLogicPath.Enabled = false;
+ this.customLogicPath.Location = new System.Drawing.Point(113, 175);
+ this.customLogicPath.Name = "customLogicPath";
+ this.customLogicPath.Size = new System.Drawing.Size(206, 20);
+ this.customLogicPath.TabIndex = 8;
+ //
+ // customLocationCheckBox
+ //
+ this.customLocationCheckBox.AutoSize = true;
+ this.customLocationCheckBox.Location = new System.Drawing.Point(5, 152);
+ this.customLocationCheckBox.Name = "customLocationCheckBox";
+ this.customLocationCheckBox.Size = new System.Drawing.Size(146, 17);
+ this.customLocationCheckBox.TabIndex = 6;
+ this.customLocationCheckBox.Text = "Use Custom Location List";
+ this.customLocationCheckBox.UseVisualStyleBackColor = true;
+ this.customLocationCheckBox.CheckedChanged += new System.EventHandler(this.CustomLocationCheckBox_CheckedChanged);
+ //
+ // mainTabs
+ //
+ this.mainTabs.Controls.Add(this.logicTab);
+ this.mainTabs.Controls.Add(this.locationsTab);
+ this.mainTabs.Location = new System.Drawing.Point(9, 27);
+ this.mainTabs.Name = "mainTabs";
+ this.mainTabs.SelectedIndex = 0;
+ this.mainTabs.Size = new System.Drawing.Size(369, 235);
+ this.mainTabs.TabIndex = 9;
+ //
+ // locationsTab
+ //
+ this.locationsTab.Controls.Add(this.customLocationCheckBox);
+ this.locationsTab.Controls.Add(this.customLocationPath);
+ this.locationsTab.Controls.Add(this.browseLocationButton);
+ this.locationsTab.Location = new System.Drawing.Point(4, 22);
+ this.locationsTab.Name = "locationsTab";
+ this.locationsTab.Padding = new System.Windows.Forms.Padding(3);
+ this.locationsTab.Size = new System.Drawing.Size(361, 209);
+ this.locationsTab.TabIndex = 1;
+ this.locationsTab.Text = "Locations";
+ this.locationsTab.UseVisualStyleBackColor = true;
//
// MainWindow
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
- this.ClientSize = new System.Drawing.Size(345, 222);
+ this.ClientSize = new System.Drawing.Size(390, 316);
+ this.Controls.Add(this.mainTabs);
this.Controls.Add(this.randomize);
this.Controls.Add(this.statusStrip1);
this.Controls.Add(this.menuStrip1);
@@ -104,6 +212,11 @@ private void InitializeComponent()
this.statusStrip1.PerformLayout();
this.menuStrip1.ResumeLayout(false);
this.menuStrip1.PerformLayout();
+ this.logicTab.ResumeLayout(false);
+ this.logicTab.PerformLayout();
+ this.mainTabs.ResumeLayout(false);
+ this.locationsTab.ResumeLayout(false);
+ this.locationsTab.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
@@ -117,6 +230,15 @@ private void InitializeComponent()
private System.Windows.Forms.ToolStripMenuItem openToolStripMenuItem;
private System.Windows.Forms.Button randomize;
private System.Windows.Forms.ToolStripStatusLabel statusText;
+ private System.Windows.Forms.TabPage logicTab;
+ private System.Windows.Forms.CheckBox customLocationCheckBox;
+ private System.Windows.Forms.TextBox customLogicPath;
+ private System.Windows.Forms.TextBox customLocationPath;
+ private System.Windows.Forms.Button browseLocationButton;
+ private System.Windows.Forms.Button browseLogicButton;
+ private System.Windows.Forms.CheckBox customLogicCheckBox;
+ private System.Windows.Forms.TabControl mainTabs;
+ private System.Windows.Forms.TabPage locationsTab;
}
}
diff --git a/UI/MainWindow.cs b/UI/MainWindow.cs
index ae65e0d2..af62c6ae 100644
--- a/UI/MainWindow.cs
+++ b/UI/MainWindow.cs
@@ -3,6 +3,7 @@
using System.ComponentModel;
using System.Data;
using System.Drawing;
+using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -15,7 +16,6 @@ namespace MinishRandomizer
public partial class MainWindow : Form
{
private ROM ROM_;
- private string locationPath;
public MainWindow()
{
@@ -34,9 +34,19 @@ private void Randomize_Click(object sender, EventArgs e)
return;
}
- Shuffler shuffler = new Shuffler();
- shuffler.LoadLocations(locationPath);
+ Shuffler shuffler = new Shuffler(Path.GetDirectoryName(ROM.Instance.path));
+
+ if (customLocationCheckBox.Checked)
+ {
+ shuffler.LoadLocations(customLocationPath.Text);
+ }
+ else
+ {
+ shuffler.LoadLocations(null);
+ }
+
+ shuffler.RandomizeLocations();
}
private void LoadRom()
@@ -69,5 +79,43 @@ private void LoadRom()
return;
}
}
+
+ private void CustomLocationCheckBox_CheckedChanged(object sender, EventArgs e)
+ {
+
+ }
+
+ private void CustomLogicCheckBox_CheckedChanged(object sender, EventArgs e)
+ {
+
+ }
+
+ private void BrowseLocationButton_Click(object sender, EventArgs e)
+ {
+ OpenFileDialog ofd = new OpenFileDialog
+ {
+ Filter = "Location Data|*.location|All Files|*.*",
+ Title = "Select Custom Locations"
+ };
+
+ if (ofd.ShowDialog() != DialogResult.OK)
+ {
+ return;
+ }
+ }
+
+ private void BrowseLogicButton_Click(object sender, EventArgs e)
+ {
+ OpenFileDialog ofd = new OpenFileDialog
+ {
+ Filter = "Logic Data|*.logic|All Files|*.*",
+ Title = "Select Custom Logic"
+ };
+
+ if (ofd.ShowDialog() != DialogResult.OK)
+ {
+ return;
+ }
+ }
}
}
From 571ba70ec5f28a826baafbd55c9827b8e23986e5 Mon Sep 17 00:00:00 2001
From: 21aslade <21aslade@go.dsdmail.net>
Date: Thu, 13 Jun 2019 14:30:45 -0600
Subject: [PATCH 03/41] Allow randomization of items with a buggy shuffle
that's likely to fail
---
Randomizer/Dependency.cs | 111 ++++++++++++++++++++++------------
Randomizer/Location.cs | 53 ++++++++++++++--
Randomizer/Shuffler.cs | 51 ++++++++++++----
Resources/default.logic | 12 +++-
Resources/icon.ico | Bin 0 -> 1078 bytes
UI/MainWindow.Designer.cs | 91 ++++++----------------------
UI/MainWindow.cs | 28 ++-------
UI/MainWindow.resx | 23 +++++++
Utilities/ExtensionMethods.cs | 15 ++++-
9 files changed, 233 insertions(+), 151 deletions(-)
create mode 100644 Resources/icon.ico
diff --git a/Randomizer/Dependency.cs b/Randomizer/Dependency.cs
index b31e4d47..adb9e378 100644
--- a/Randomizer/Dependency.cs
+++ b/Randomizer/Dependency.cs
@@ -20,6 +20,8 @@ public static List GetDependencies(string logic)
{
List dependencies = new List();
+ logic = logic.Replace(" ", "");
+
// Match: comma, capture and match anything between
string regexPattern = @"(?:,|^)\(([&|](?:[^()]|(?
\()|(?<-p>\)))+)\)(?:,|$)|,";
string[] subLogic = Regex.Split(logic, regexPattern);
@@ -30,6 +32,7 @@ public static List GetDependencies(string logic)
continue;
}
+
switch(sequence[0])
{
// If the first character of the string is & or |, it's a compound dependency
@@ -42,7 +45,33 @@ public static List GetDependencies(string logic)
dependencies.Add(orDependency);
break;
default:
-
+ string[] dependencyParts = sequence.Split('.');
+ switch (dependencyParts[0])
+ {
+ case "Locations":
+ LocationDependency locationDependency = new LocationDependency(dependencyParts[1]);
+ dependencies.Add(locationDependency);
+ break;
+ case "Items":
+ if (Enum.TryParse(dependencyParts[1], out ItemType type))
+ {
+ byte subType = 0;
+ if (dependencyParts.Length >= 3)
+ {
+ if (!byte.TryParse(dependencyParts[2], NumberStyles.HexNumber, null, out subType))
+ {
+ if (Enum.TryParse(dependencyParts[2], out KinstoneType subKinstoneType))
+ {
+ subType = (byte)subKinstoneType;
+ }
+ }
+ }
+
+ ItemDependency itemDependency = new ItemDependency(new Item(type, subType));
+ dependencies.Add(itemDependency);
+ }
+ break;
+ }
break;
}
}
@@ -50,50 +79,58 @@ public static List GetDependencies(string logic)
return dependencies;
}
- private Item RequiredItem;
-
- public Dependency()
+ public virtual bool DependencyFulfilled(List- availableItems, List locations)
{
+ return false;
+ }
+
+ }
+ public class ItemDependency : Dependency
+ {
+ private Item RequiredItem;
+ public ItemDependency(Item item)
+ {
+ RequiredItem = item;
}
- public Dependency(string dependencyText)
+ public override bool DependencyFulfilled(List
- availableItems, List locations)
{
- string[] dependencyParts = dependencyText.Split('.');
- switch(dependencyParts[0].ToLower())
+ foreach (Item item in availableItems)
{
- case "locations":
-
- break;
- case "items":
- if (Enum.TryParse(dependencyParts[1], out ItemType type))
- {
- byte subType = 0;
- if (dependencyParts.Length >= 2)
- {
- if (!byte.TryParse(dependencyParts[2], NumberStyles.HexNumber, null, out subType))
- {
- if (Enum.TryParse(dependencyParts[2], out KinstoneType subKinstoneType))
- {
- subType = (byte)subKinstoneType;
- }
- }
- }
- RequiredItem = new Item(type, subType);
- }
- else
- {
- RequiredItem = new Item(ItemType.Untyped, 0);
- }
- break;
+ Console.WriteLine($"Has: {item.Type.ToString()} Needs: {RequiredItem.Type.ToString()}");
+ if (item.Type == RequiredItem.Type && item.SubValue == RequiredItem.SubValue)
+ {
+ return true;
+ }
}
+
+ Console.WriteLine($"Inccessible because: {RequiredItem.Type.ToString()} is not available!");
+ return false;
}
+ }
- public virtual bool DependencyFulfilled(List
- unplacedItems)
+ public class LocationDependency : Dependency
+ {
+ private string RequiredLocationName;
+ public LocationDependency(string locationName)
{
- return false;
+ RequiredLocationName = locationName;
}
+ public override bool DependencyFulfilled(List
- availableItems, List locations)
+ {
+ foreach (Location location in locations)
+ {
+ if (location.Name == RequiredLocationName)
+ {
+ Console.WriteLine($"Sub-Evaluating: {RequiredLocationName}");
+ return location.CanPlace(new Item(), availableItems, locations);
+ }
+ }
+ Console.WriteLine($"Could not find location: {RequiredLocationName}");
+ return false;
+ }
}
public class AndDependency : Dependency
@@ -105,11 +142,11 @@ public AndDependency(string dependencyText)
AndList = GetDependencies(dependencyText);
}
- override public bool DependencyFulfilled(List
- unplacedItems)
+ public override bool DependencyFulfilled(List
- availableItems, List locations)
{
foreach (Dependency dependency in AndList)
{
- if (dependency.DependencyFulfilled(unplacedItems) == false)
+ if (dependency.DependencyFulfilled(availableItems, locations) == false)
{
return false;
}
@@ -127,11 +164,11 @@ public OrDependency(string dependencyText)
OrList = GetDependencies(dependencyText);
}
- override public bool DependencyFulfilled(List
- unplacedItems)
+ public override bool DependencyFulfilled(List
- availableItems, List locations)
{
foreach (Dependency dependency in OrList)
{
- if (dependency.DependencyFulfilled(unplacedItems) == true)
+ if (dependency.DependencyFulfilled(availableItems, locations) == true)
{
return true;
}
diff --git a/Randomizer/Location.cs b/Randomizer/Location.cs
index 1cddc61f..5b04fdd2 100644
--- a/Randomizer/Location.cs
+++ b/Randomizer/Location.cs
@@ -46,7 +46,34 @@ public static Location GetLocation(string locationText)
List dependencies = Dependency.GetDependencies(logic);
- return new Location(type, name, address, large, dependencies);
+ Location location = new Location(type, name, address, large, dependencies);
+
+ if (locationParts.Length >= 6)
+ {
+ string[] subParts = locationParts[5].Split('.');
+
+ if (subParts[0] == "Items")
+ {
+ if (Enum.TryParse(subParts[1], out ItemType replacementType))
+ {
+ byte subType = 0;
+ if (subParts.Length >= 3)
+ {
+ if (!byte.TryParse(subParts[2], NumberStyles.HexNumber, null, out subType))
+ {
+ if (Enum.TryParse(subParts[2], out KinstoneType subKinstoneType))
+ {
+ subType = (byte)subKinstoneType;
+ }
+ }
+ }
+
+ location.SetItem(new Item(replacementType, subType));
+ }
+ }
+ }
+
+ return location;
}
public static int GetAddressFromString(string addressString)
@@ -94,7 +121,7 @@ public enum LocationType
NPCItem,
KinstoneItem,
HeartPieceItem,
- HelperItem
+ Helper
}
public List Dependencies;
@@ -128,6 +155,11 @@ public Location(LocationType type, string name, int address, bool large, List unplacedItems)
+ public bool CanPlace(Item itemToPlace, List
- availableItems, List locations)
{
+ Console.WriteLine($"Evaluating: {Name}");
+ if (!Large && itemToPlace.SubValue != 0)
+ {
+ Console.WriteLine($"Can't place because {Name} is small!");
+ return false;
+ }
foreach (Dependency dependency in Dependencies)
{
- if (!dependency.DependencyFulfilled(unplacedItems))
+ if (!dependency.DependencyFulfilled(availableItems, locations))
{
return false;
}
@@ -163,8 +201,13 @@ public bool IsAccessible(List
- unplacedItems)
public void Fill(Item contents)
{
- Contents = contents;
+ SetItem(contents);
Filled = true;
}
+
+ public void SetItem(Item contents)
+ {
+ Contents = contents;
+ }
}
}
diff --git a/Randomizer/Shuffler.cs b/Randomizer/Shuffler.cs
index 6f021a50..b81f5ad2 100644
--- a/Randomizer/Shuffler.cs
+++ b/Randomizer/Shuffler.cs
@@ -69,6 +69,9 @@ public void SetSeed(int seed)
public void LoadLocations(string locationFile)
{
+ Locations.Clear();
+ Items.Clear();
+
string[] locationStrings;
if (locationFile == null)
@@ -78,7 +81,7 @@ public void LoadLocations(string locationFile)
using (StreamReader reader = new StreamReader(stream))
{
string allLocations = reader.ReadToEnd();
- locationStrings = allLocations.Split('\n');
+ locationStrings = allLocations.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None);
}
}
else
@@ -88,27 +91,51 @@ public void LoadLocations(string locationFile)
foreach (string locationString in locationStrings)
{
+ if (locationString == "" || locationString[0] == '#')
+ {
+ continue;
+ }
+
Location newLocation = Location.GetLocation(locationString);
Locations.Add(newLocation);
- Items.Add(newLocation.Contents);
+ if (newLocation.Type != Location.LocationType.Helper)
+ {
+ Items.Add(newLocation.Contents);
+ }
}
}
public void RandomizeLocations()
{
- List unfilledLocations = Locations.Where(location => !location.Filled).ToList();
- while (unfilledLocations.Count > 0)
+ List
- unplacedItems = Items.ToList();
+ List unfilledLocations = Locations.Where(location => !location.Filled && location.Type != Location.LocationType.Helper).ToList();
+ unfilledLocations.Shuffle(RNG);
+ unplacedItems.Shuffle(RNG);
+
+ int itemIndex;
+ while (unplacedItems.Count > 0)
{
- Location location = unfilledLocations[RNG.Next(unfilledLocations.Count)];
- Item item = Items[RNG.Next(Items.Count)];
- List
- unplacedItems = Items.Where(listItem => !ReferenceEquals(listItem, item)).ToList();
- if (location.IsAccessible(unplacedItems))
+ // TODO: Make this fill not bad
+ itemIndex = RNG.Next(unplacedItems.Count);
+ Item item = unplacedItems[itemIndex];
+ Console.WriteLine($"Placing: {item.Type.ToString()}");
+ unplacedItems.RemoveAt(itemIndex);
+ List availableLocations = unfilledLocations.Where(location => location.CanPlace(item, unplacedItems, unfilledLocations)).ToList();
+
+ if (availableLocations.Count <= 0)
{
- Console.WriteLine($"Loc: {location.Name} Item: {item.Type.ToString()}");
- Items.Remove(item);
- location.Fill(item);
- unfilledLocations.Remove(location);
+ Console.WriteLine($"Could not place {item.Type.ToString()}!");
+ return;
}
+
+ availableLocations[0].Fill(item);
+ Console.WriteLine($"Placed {item.Type.ToString()} at {availableLocations[0].Name} with {unplacedItems.Count} items remaining\n");
+ unfilledLocations.Remove(availableLocations[0]);
+ }
+
+ if (unfilledLocations.Count != 0)
+ {
+ Console.WriteLine("Not all locations filled!");
}
using (MemoryStream ms = new MemoryStream(ROM.Instance.romData))
diff --git a/Resources/default.logic b/Resources/default.logic
index 4bcacd0b..811f9014 100644
--- a/Resources/default.logic
+++ b/Resources/default.logic
@@ -1,2 +1,10 @@
-Smith's House;Minor;22-11-00;true
-Shield Location;Normal;0000FA6A;true
\ No newline at end of file
+# Default logic for the randomizer
+
+SmithHouse;Minor;22-11-00;true
+
+# Deepwood locations
+# Need gust jar to leave room
+DeepwoodWiggler;Normal;48-00-01;true;Items.GustJar
+DeepwoodButtonShells1;Minor;48-01-01;true;Items.GustJar
+DeepwoodButtonShells2;Minor;48-01-02;true;Items.GustJar
+DeepwoodPreCompassShells;Minor;48-02-01;true;Items.GustJar
\ No newline at end of file
diff --git a/Resources/icon.ico b/Resources/icon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..3858b6c8f8fa670bc1f680d205b1b7221ce59421
GIT binary patch
literal 1078
zcmcIjyKciU3_QmHnyHaGb*pOxMMk6Y9=p|t^ppBY`4o>GGiC_j9wk3y8qHP#2kOWp
zDOxg+VS}PDv|IdafHwffM1IVG#~e7)R}c*$y1Q%lh28TvOm$|$^{({qV5ve2UOMl6
zB2e7<(%K}fxbs$p7raa>vx?@vbh4)UD{g%nhF@_odv58i=5&C9p#x95lu6Rlvc
zF#3Zo%?p8*TH-im)|R
zCbqFL{%*HHcgUVZj=UMJur30h`h?;72jE6r_rT>PqT{}!ujBt7Ap0aPTFH^zU)p(g
AW&i*H
literal 0
HcmV?d00001
diff --git a/UI/MainWindow.Designer.cs b/UI/MainWindow.Designer.cs
index 25d2e099..3fc1a7ac 100644
--- a/UI/MainWindow.Designer.cs
+++ b/UI/MainWindow.Designer.cs
@@ -28,6 +28,7 @@ protected override void Dispose(bool disposing)
///
private void InitializeComponent()
{
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainWindow));
this.statusStrip1 = new System.Windows.Forms.StatusStrip();
this.statusText = new System.Windows.Forms.ToolStripStatusLabel();
this.menuStrip1 = new System.Windows.Forms.MenuStrip();
@@ -35,19 +36,14 @@ private void InitializeComponent()
this.openToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.randomize = new System.Windows.Forms.Button();
this.logicTab = new System.Windows.Forms.TabPage();
- this.customLogicCheckBox = new System.Windows.Forms.CheckBox();
- this.customLocationPath = new System.Windows.Forms.TextBox();
- this.browseLogicButton = new System.Windows.Forms.Button();
- this.browseLocationButton = new System.Windows.Forms.Button();
this.customLogicPath = new System.Windows.Forms.TextBox();
- this.customLocationCheckBox = new System.Windows.Forms.CheckBox();
+ this.browseLogicButton = new System.Windows.Forms.Button();
+ this.customLogicCheckBox = new System.Windows.Forms.CheckBox();
this.mainTabs = new System.Windows.Forms.TabControl();
- this.locationsTab = new System.Windows.Forms.TabPage();
this.statusStrip1.SuspendLayout();
this.menuStrip1.SuspendLayout();
this.logicTab.SuspendLayout();
this.mainTabs.SuspendLayout();
- this.locationsTab.SuspendLayout();
this.SuspendLayout();
//
// statusStrip1
@@ -113,24 +109,13 @@ private void InitializeComponent()
this.logicTab.Text = "Logic";
this.logicTab.UseVisualStyleBackColor = true;
//
- // customLogicCheckBox
- //
- this.customLogicCheckBox.AutoSize = true;
- this.customLogicCheckBox.Location = new System.Drawing.Point(5, 152);
- this.customLogicCheckBox.Name = "customLogicCheckBox";
- this.customLogicCheckBox.Size = new System.Drawing.Size(112, 17);
- this.customLogicCheckBox.TabIndex = 5;
- this.customLogicCheckBox.Text = "Use Custom Logic";
- this.customLogicCheckBox.UseVisualStyleBackColor = true;
- this.customLogicCheckBox.CheckedChanged += new System.EventHandler(this.CustomLogicCheckBox_CheckedChanged);
- //
- // customLocationPath
+ // customLogicPath
//
- this.customLocationPath.Enabled = false;
- this.customLocationPath.Location = new System.Drawing.Point(113, 175);
- this.customLocationPath.Name = "customLocationPath";
- this.customLocationPath.Size = new System.Drawing.Size(206, 20);
- this.customLocationPath.TabIndex = 3;
+ this.customLogicPath.Enabled = false;
+ this.customLogicPath.Location = new System.Drawing.Point(113, 175);
+ this.customLogicPath.Name = "customLogicPath";
+ this.customLogicPath.Size = new System.Drawing.Size(206, 20);
+ this.customLogicPath.TabIndex = 8;
//
// browseLogicButton
//
@@ -143,59 +128,26 @@ private void InitializeComponent()
this.browseLogicButton.UseVisualStyleBackColor = true;
this.browseLogicButton.Click += new System.EventHandler(this.BrowseLogicButton_Click);
//
- // browseLocationButton
- //
- this.browseLocationButton.Enabled = false;
- this.browseLocationButton.Location = new System.Drawing.Point(32, 175);
- this.browseLocationButton.Name = "browseLocationButton";
- this.browseLocationButton.Size = new System.Drawing.Size(75, 23);
- this.browseLocationButton.TabIndex = 4;
- this.browseLocationButton.Text = "Browse...";
- this.browseLocationButton.UseVisualStyleBackColor = true;
- this.browseLocationButton.Click += new System.EventHandler(this.BrowseLocationButton_Click);
- //
- // customLogicPath
- //
- this.customLogicPath.Enabled = false;
- this.customLogicPath.Location = new System.Drawing.Point(113, 175);
- this.customLogicPath.Name = "customLogicPath";
- this.customLogicPath.Size = new System.Drawing.Size(206, 20);
- this.customLogicPath.TabIndex = 8;
- //
- // customLocationCheckBox
+ // customLogicCheckBox
//
- this.customLocationCheckBox.AutoSize = true;
- this.customLocationCheckBox.Location = new System.Drawing.Point(5, 152);
- this.customLocationCheckBox.Name = "customLocationCheckBox";
- this.customLocationCheckBox.Size = new System.Drawing.Size(146, 17);
- this.customLocationCheckBox.TabIndex = 6;
- this.customLocationCheckBox.Text = "Use Custom Location List";
- this.customLocationCheckBox.UseVisualStyleBackColor = true;
- this.customLocationCheckBox.CheckedChanged += new System.EventHandler(this.CustomLocationCheckBox_CheckedChanged);
+ this.customLogicCheckBox.AutoSize = true;
+ this.customLogicCheckBox.Location = new System.Drawing.Point(5, 152);
+ this.customLogicCheckBox.Name = "customLogicCheckBox";
+ this.customLogicCheckBox.Size = new System.Drawing.Size(112, 17);
+ this.customLogicCheckBox.TabIndex = 5;
+ this.customLogicCheckBox.Text = "Use Custom Logic";
+ this.customLogicCheckBox.UseVisualStyleBackColor = true;
+ this.customLogicCheckBox.CheckedChanged += new System.EventHandler(this.CustomLogicCheckBox_CheckedChanged);
//
// mainTabs
//
this.mainTabs.Controls.Add(this.logicTab);
- this.mainTabs.Controls.Add(this.locationsTab);
this.mainTabs.Location = new System.Drawing.Point(9, 27);
this.mainTabs.Name = "mainTabs";
this.mainTabs.SelectedIndex = 0;
this.mainTabs.Size = new System.Drawing.Size(369, 235);
this.mainTabs.TabIndex = 9;
//
- // locationsTab
- //
- this.locationsTab.Controls.Add(this.customLocationCheckBox);
- this.locationsTab.Controls.Add(this.customLocationPath);
- this.locationsTab.Controls.Add(this.browseLocationButton);
- this.locationsTab.Location = new System.Drawing.Point(4, 22);
- this.locationsTab.Name = "locationsTab";
- this.locationsTab.Padding = new System.Windows.Forms.Padding(3);
- this.locationsTab.Size = new System.Drawing.Size(361, 209);
- this.locationsTab.TabIndex = 1;
- this.locationsTab.Text = "Locations";
- this.locationsTab.UseVisualStyleBackColor = true;
- //
// MainWindow
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@@ -205,6 +157,7 @@ private void InitializeComponent()
this.Controls.Add(this.randomize);
this.Controls.Add(this.statusStrip1);
this.Controls.Add(this.menuStrip1);
+ this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MainMenuStrip = this.menuStrip1;
this.Name = "MainWindow";
this.Text = "Minish Cap Randomizer";
@@ -215,8 +168,6 @@ private void InitializeComponent()
this.logicTab.ResumeLayout(false);
this.logicTab.PerformLayout();
this.mainTabs.ResumeLayout(false);
- this.locationsTab.ResumeLayout(false);
- this.locationsTab.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
@@ -231,14 +182,10 @@ private void InitializeComponent()
private System.Windows.Forms.Button randomize;
private System.Windows.Forms.ToolStripStatusLabel statusText;
private System.Windows.Forms.TabPage logicTab;
- private System.Windows.Forms.CheckBox customLocationCheckBox;
private System.Windows.Forms.TextBox customLogicPath;
- private System.Windows.Forms.TextBox customLocationPath;
- private System.Windows.Forms.Button browseLocationButton;
private System.Windows.Forms.Button browseLogicButton;
private System.Windows.Forms.CheckBox customLogicCheckBox;
private System.Windows.Forms.TabControl mainTabs;
- private System.Windows.Forms.TabPage locationsTab;
}
}
diff --git a/UI/MainWindow.cs b/UI/MainWindow.cs
index af62c6ae..7e472c50 100644
--- a/UI/MainWindow.cs
+++ b/UI/MainWindow.cs
@@ -37,9 +37,9 @@ private void Randomize_Click(object sender, EventArgs e)
Shuffler shuffler = new Shuffler(Path.GetDirectoryName(ROM.Instance.path));
- if (customLocationCheckBox.Checked)
+ if (customLogicCheckBox.Checked)
{
- shuffler.LoadLocations(customLocationPath.Text);
+ shuffler.LoadLocations(customLogicPath.Text);
}
else
{
@@ -80,28 +80,10 @@ private void LoadRom()
}
}
- private void CustomLocationCheckBox_CheckedChanged(object sender, EventArgs e)
- {
-
- }
-
private void CustomLogicCheckBox_CheckedChanged(object sender, EventArgs e)
{
-
- }
-
- private void BrowseLocationButton_Click(object sender, EventArgs e)
- {
- OpenFileDialog ofd = new OpenFileDialog
- {
- Filter = "Location Data|*.location|All Files|*.*",
- Title = "Select Custom Locations"
- };
-
- if (ofd.ShowDialog() != DialogResult.OK)
- {
- return;
- }
+ browseLogicButton.Enabled = customLogicCheckBox.Checked;
+ customLogicPath.Enabled = customLogicCheckBox.Checked;
}
private void BrowseLogicButton_Click(object sender, EventArgs e)
@@ -116,6 +98,8 @@ private void BrowseLogicButton_Click(object sender, EventArgs e)
{
return;
}
+
+ customLogicPath.Text = ofd.FileName;
}
}
}
diff --git a/UI/MainWindow.resx b/UI/MainWindow.resx
index 6beaffa3..0ba25bb6 100644
--- a/UI/MainWindow.resx
+++ b/UI/MainWindow.resx
@@ -123,4 +123,27 @@
133, 17
+
+
+
+ AAABAAIAICAQAAEABADoAgAAJgAAABAQEAABAAQAKAEAAA4DAAAoAAAAIAAAAEAAAAABAAQAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIYAAIGJAAEDDAAABA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiAAAAAAAAAAAAAAAAAAAAIgAAAAAAAAAAAAAA
+ ACIzREREAAAAAAAAAAAAAAAiM0RERAAAAAAAAAAAAAAzRCIREQAAAAAAAAAAAAAAM0QiEREAAAAAAAAA
+ AAAAM0QREREAAAAAAAAAAAAAADNEERERAAAAAAAAAAAAACJEAAAAAAAAAAAAAAAAAAAiRAAAAAAAAAAA
+ AAAAAAAARCIzMzMzMwAAAAAAAAAAAEQiMzMzMzMAAAAAAAAAAABEEUQzMzMAAAAAAAAAAAAARBFEMzMz
+ AAAAAAAAAAAAAEQRADMzABEREQAAAAAAAABEEQAzMwAREREAAAAAAAAARBERADMAABERAAAAAAAAAEQR
+ EQAzAAAREQAAAAAAAABEABERMzMzAAAAAAAAAAAARAARETMzMwAAAAAAAAAAADMiEREARDMAAAAAAAAA
+ AAAzIhERAEQzAAAAAAAAAAAAIkQAEREiAAAAAAAAAAAAACJEABERIgAAAAAAAAAAAAAAM0QAEREAAAAA
+ AAAAAAAAADNEABERAAAAAAAAAAAAAAAAM0QiAAAAAAAAAAAAAAAAADNEIgAAAAAAAAAAAAAAAAAAIjNE
+ REQAAAAAAAAAAAAAACIzREREAAAAAAAAAAAAAAAAAAAiAAAAAAAAAAAAAAAAAAAAIgAAAAAA//8AP///
+ AD//8AA///AAP//AAP//wAD//wAD//8AA///AAP//wAD//wAAP/8AAD//AAAP/wAAD/8AAAP/AAAD/wA
+ AA/8AAAP/AAAP/wAAD/8AAD//AAA//8AA///AAP//wAD//8AA///wAD//8AA///wAD//8AA///8AP///
+ AD8oAAAAEAAAACAAAAABAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIYAAIGJAAEDDAAABA
+ +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAjRE
+ AAAAAAA0IRAAAAAAA0ERAAAAAAAkAAAAAAAAAEIzMzAAAAAAQUMzAAAAAABBAzAREAAAAEEQMAEQAAAA
+ QBEzMAAAAAAyEQQwAAAAACQBEgAAAAAAA0ARAAAAAAAANCAAAAAAAAACNEQAAAAAAAAAIAAA/wcAAPwH
+ AAD4DwAA8B8AAPAfAADgDwAA4AcAAOADAADgAwAA4AcAAOAPAADwHwAA8B8AAPgPAAD8BwAA/wcAAA==
+
+
\ No newline at end of file
diff --git a/Utilities/ExtensionMethods.cs b/Utilities/ExtensionMethods.cs
index a2a0b49e..5d1185b9 100644
--- a/Utilities/ExtensionMethods.cs
+++ b/Utilities/ExtensionMethods.cs
@@ -32,5 +32,18 @@ public static string Hex( this byte num )
{
return Convert.ToString( num, 16 ).ToUpper();
}
- }
+
+ public static void Shuffle(this IList list, Random rng)
+ {
+ int n = list.Count;
+ while (n > 1)
+ {
+ n--;
+ int k = rng.Next(n + 1);
+ T value = list[k];
+ list[k] = list[n];
+ list[n] = value;
+ }
+ }
+ }
}
From 579ec6d7a7b16d5fc3ce197f6ac1fa07390f63e8 Mon Sep 17 00:00:00 2001
From: 21aslade <21aslade@go.dsdmail.net>
Date: Thu, 13 Jun 2019 18:37:15 -0600
Subject: [PATCH 04/41] Improve filler, logic; enumerate all valid items
---
Core/Constants.cs | 56 ++++++++++++++++++++++++++---
Randomizer/Location.cs | 52 ++++++++++-----------------
Randomizer/Shuffler.cs | 79 ++++++++++++++++++++++++++++-------------
Resources/default.logic | 28 +++++++++++----
4 files changed, 146 insertions(+), 69 deletions(-)
diff --git a/Core/Constants.cs b/Core/Constants.cs
index f63255c3..78f26977 100644
--- a/Core/Constants.cs
+++ b/Core/Constants.cs
@@ -139,11 +139,35 @@ public enum ItemType
BottleCharmFarore = 0x30,
BottleCharmDin = 0x31,
- ShellsX = 0x3F,
+ SmithSwordQuest = 0x34,
+ BrokenPicoriBlade = 0x35,
+ DogFoodBottle = 0x36,
+ LonLonKey = 0x37,
+ WakeUpMushroom = 0x38,
+ HyruleanBestiary = 0x39,
+ PicoriLegend = 0x3A,
+ MaskHistory = 0x3B,
+ GraveyardKey = 0x3C,
+ TingleTrophy = 0x3D,
+ CarlovMedal = 0x3E,
+ ShellsX = 0x3F,
+ EarthElement = 0x40,
+ FireElement = 0x41,
+ WaterElement = 0x42,
+ WindElement = 0x43,
+ GripRing = 0x44,
PowerBracelets = 0x45,
- Flippers = 0X46,
-
+ Flippers = 0x46,
+ HyruleMap = 0x47,
+ SpinAttack = 0x48,
+ RollAttack = 0x49,
+ DashAttack = 0x4A,
+ RockBreaker = 0x4B,
+ SwordBeam = 0x4C,
+ GreatSpin = 0x4D,
+ DownThrust = 0x4E,
+ PerilBeam = 0x4F,
DungeonMap = 0x50,
Compass = 0x51,
BigKey = 0x52,
@@ -155,11 +179,33 @@ public enum ItemType
Rupee100 = 0x58,
Rupee200 = 0x59,
+ JabberNut = 0x5B,
KinstoneX = 0x5C,
-
+ Bombs5 = 0x5D,
+ Arrows5 = 0x5E,
+ SmallHeart = 0x5F,
+ Fairy = 0x60,
+ Shells30 = 0x61,
+ HeartContainer = 0x62,
PieceOfHeart = 0x63,
Wallet = 0x64,
-
+ BigBomb = 0x65,
+ LargeQuiver = 0x66,
+ KinstoneBag = 0x67,
+ Brioche = 0x68,
+ Croissant = 0x69,
+ PieSlice = 0x6A,
+ CakeSlice = 0x6B,
+ Bombs10 = 0x6C,
+ Bombs30 = 0x6D,
+ Arrows10 = 0x6E,
+ Arrows30 = 0x6F,
+ ArrowButterfly = 0x70,
+ DigButterfly = 0x71,
+ SwimButterfly = 0x72,
+ FastSplin = 0x73,
+ FastSplit = 0x74,
+ LongSpin = 0x75
}
public class Header
diff --git a/Randomizer/Location.cs b/Randomizer/Location.cs
index 5b04fdd2..6b5ebd3f 100644
--- a/Randomizer/Location.cs
+++ b/Randomizer/Location.cs
@@ -17,9 +17,9 @@ public static Location GetLocation(string locationText)
// Location format: Type;Name;Address;Large;Logic
string[] locationParts = locationText.Split(';');
- if (locationParts.Length < 4)
+ if (locationParts.Length < 3)
{
- return new Location(LocationType.Untyped, "INVALID LOCATION", 0, false, null);
+ return new Location(LocationType.Untyped, "INVALID LOCATION", 0, null);
}
string name = locationParts[0];
@@ -27,30 +27,24 @@ public static Location GetLocation(string locationText)
string locationType = locationParts[1];
if (!Enum.TryParse(locationType, out LocationType type) || type == LocationType.Untyped)
{
- return new Location(LocationType.Untyped, "INVALID LOCATION", 0, false, null);
+ return new Location(LocationType.Untyped, "INVALID LOCATION", 0, null);
}
int address = GetAddressFromString(locationParts[2]);
- string largeText = locationParts[3];
- if (!bool.TryParse(largeText, out bool large))
- {
- large = false;
- }
-
string logic = "";
- if (locationParts.Length >= 5)
+ if (locationParts.Length >= 4)
{
- logic = locationParts[4];
+ logic = locationParts[3];
}
List dependencies = Dependency.GetDependencies(logic);
- Location location = new Location(type, name, address, large, dependencies);
+ Location location = new Location(type, name, address, dependencies);
- if (locationParts.Length >= 6)
+ if (locationParts.Length >= 5)
{
- string[] subParts = locationParts[5].Split('.');
+ string[] subParts = locationParts[4].Split('.');
if (subParts[0] == "Items")
{
@@ -129,19 +123,16 @@ public enum LocationType
public string Name;
public bool Filled;
public Item Contents { get; private set; }
- private bool Large;
private Item DefaultContents;
private int Address;
- public Location(LocationType type, string name, int address, bool large, List dependencies)
+ public Location(LocationType type, string name, int address, List dependencies)
{
Type = type;
Name = name;
Address = address;
- Large = large;
-
Dependencies = dependencies;
if (address != 0)
@@ -155,27 +146,27 @@ public Location(LocationType type, string name, int address, bool large, List availableItems, List locations)
{
Console.WriteLine($"Evaluating: {Name}");
- if (!Large && itemToPlace.SubValue != 0)
- {
- Console.WriteLine($"Can't place because {Name} is small!");
- return false;
- }
foreach (Dependency dependency in Dependencies)
{
if (!dependency.DependencyFulfilled(availableItems, locations))
diff --git a/Randomizer/Shuffler.cs b/Randomizer/Shuffler.cs
index b81f5ad2..bbf2c05b 100644
--- a/Randomizer/Shuffler.cs
+++ b/Randomizer/Shuffler.cs
@@ -51,14 +51,16 @@ public class Shuffler
private Random RNG;
private List Locations;
- private List
- Items;
+ private List
- MajorItems;
+ private List
- MinorItems;
private string OutputDirectory;
public Shuffler(string outputDirectory)
{
RNG = new Random();
Locations = new List();
- Items = new List
- ();
+ MajorItems = new List
- ();
+ MinorItems = new List
- ();
OutputDirectory = outputDirectory;
}
@@ -70,7 +72,8 @@ public void SetSeed(int seed)
public void LoadLocations(string locationFile)
{
Locations.Clear();
- Items.Clear();
+ MajorItems.Clear();
+ MinorItems.Clear();
string[] locationStrings;
@@ -98,41 +101,35 @@ public void LoadLocations(string locationFile)
Location newLocation = Location.GetLocation(locationString);
Locations.Add(newLocation);
- if (newLocation.Type != Location.LocationType.Helper)
+ switch(newLocation.Type)
{
- Items.Add(newLocation.Contents);
+ case Location.LocationType.Untyped:
+ case Location.LocationType.Helper:
+ break;
+ case Location.LocationType.Minor:
+ MinorItems.Add(newLocation.Contents);
+ break;
+ case Location.LocationType.Normal:
+ default:
+ Console.WriteLine($"Hey! {newLocation.Contents.Type.ToString()}");
+ MajorItems.Add(newLocation.Contents);
+ break;
}
}
}
public void RandomizeLocations()
{
- List
- unplacedItems = Items.ToList();
+ List
- unplacedItems = MajorItems.ToList();
List unfilledLocations = Locations.Where(location => !location.Filled && location.Type != Location.LocationType.Helper).ToList();
unfilledLocations.Shuffle(RNG);
unplacedItems.Shuffle(RNG);
- int itemIndex;
- while (unplacedItems.Count > 0)
- {
- // TODO: Make this fill not bad
- itemIndex = RNG.Next(unplacedItems.Count);
- Item item = unplacedItems[itemIndex];
- Console.WriteLine($"Placing: {item.Type.ToString()}");
- unplacedItems.RemoveAt(itemIndex);
- List availableLocations = unfilledLocations.Where(location => location.CanPlace(item, unplacedItems, unfilledLocations)).ToList();
+ FillLocations(MajorItems.ToList(), unfilledLocations);
- if (availableLocations.Count <= 0)
- {
- Console.WriteLine($"Could not place {item.Type.ToString()}!");
- return;
- }
+ unfilledLocations.Shuffle(RNG);
+ FastFillLocations(MinorItems.ToList(), unfilledLocations);
- availableLocations[0].Fill(item);
- Console.WriteLine($"Placed {item.Type.ToString()} at {availableLocations[0].Name} with {unplacedItems.Count} items remaining\n");
- unfilledLocations.Remove(availableLocations[0]);
- }
-
if (unfilledLocations.Count != 0)
{
Console.WriteLine("Not all locations filled!");
@@ -149,5 +146,37 @@ public void RandomizeLocations()
File.WriteAllBytes(OutputDirectory + "/mcrando.gba", ROM.Instance.romData);
}
+
+ private void FillLocations(List
- items, List locations)
+ {
+ int itemIndex;
+ for (int i = items.Count - 1; i >= 0; i--)
+ {
+ itemIndex = RNG.Next(items.Count);
+ Item item = items[itemIndex];
+ Console.WriteLine($"Placing: {item.Type.ToString()}");
+ items.RemoveAt(itemIndex);
+ List availableLocations = locations.Where(location => location.CanPlace(item, items, locations)).ToList();
+
+ if (availableLocations.Count <= 0)
+ {
+ Console.WriteLine($"Could not place {item.Type.ToString()}!");
+ return;
+ }
+
+ availableLocations[0].Fill(item);
+ Console.WriteLine($"Placed {item.Type.ToString()} at {availableLocations[0].Name} with {items.Count} items remaining\n");
+ locations.Remove(availableLocations[0]);
+ }
+ }
+
+ private void FastFillLocations(List
- items, List locations)
+ {
+ foreach (Item item in items)
+ {
+ locations[0].Fill(item);
+ locations.RemoveAt(0);
+ }
+ }
}
}
diff --git a/Resources/default.logic b/Resources/default.logic
index 811f9014..7516a489 100644
--- a/Resources/default.logic
+++ b/Resources/default.logic
@@ -1,10 +1,26 @@
-# Default logic for the randomizer
+# Default logic for the randomizer - Doesn't allow glitches, but allows unintuitive tricks
-SmithHouse;Minor;22-11-00;true
+# Location format:
+# Name;Type;Address[;Logic][;Item Override]
+# Name can be anything unique, hopefully without spaces
+# Type should be one of Location.LocationType
+# Address can be a hexadecimal address or of the form area-room-chest
+# Logic should be a comma separated series of conditions, which can take the forms:
+# (&a,b,c...) and (|a,b,c...) are an and/or of the contents
+# Items.(itemName)[.subValue] requires the given item for the location
+# Locations.(locationName) requires the location to be accessible first
+
+SmithHouse;Minor;22-11-00
# Deepwood locations
# Need gust jar to leave room
-DeepwoodWiggler;Normal;48-00-01;true;Items.GustJar
-DeepwoodButtonShells1;Minor;48-01-01;true;Items.GustJar
-DeepwoodButtonShells2;Minor;48-01-02;true;Items.GustJar
-DeepwoodPreCompassShells;Minor;48-02-01;true;Items.GustJar
\ No newline at end of file
+DeepwoodWiggler;Normal;48-00-01;Items.GustJar
+DeepwoodButtonShells1;Minor;48-01-01;Items.GustJar
+DeepwoodButtonShells2;Minor;48-01-02;Items.GustJar
+DeepwoodPreCompassShells;Minor;48-02-01;Items.GustJar
+DeepwoodBarrelShells;Minor;48-06-01;Items.GustJar
+DeepwoodUpstairsChest;Minor;48-17-01;
+
+# Cave of Flames locations
+CoFAccess;Helper;0;
+CoFBigChest
\ No newline at end of file
From d6d9f1126c37df5298c5b790cfe4b9ea4184208d Mon Sep 17 00:00:00 2001
From: 21aslade <21aslade@go.dsdmail.net>
Date: Fri, 14 Jun 2019 12:40:48 -0600
Subject: [PATCH 05/41] Update logic; still fails to generate seed
---
Core/Constants.cs | 2 +-
Randomizer/Location.cs | 12 ++++++++++++
Randomizer/Shuffler.cs | 15 ++++++++-------
Resources/default.logic | 36 +++++++++++++++++++++++++++---------
4 files changed, 48 insertions(+), 17 deletions(-)
diff --git a/Core/Constants.cs b/Core/Constants.cs
index 78f26977..7405496f 100644
--- a/Core/Constants.cs
+++ b/Core/Constants.cs
@@ -189,7 +189,7 @@ public enum ItemType
HeartContainer = 0x62,
PieceOfHeart = 0x63,
Wallet = 0x64,
- BigBomb = 0x65,
+ BombBag = 0x65,
LargeQuiver = 0x66,
KinstoneBag = 0x67,
Brioche = 0x68,
diff --git a/Randomizer/Location.cs b/Randomizer/Location.cs
index 6b5ebd3f..7116d3de 100644
--- a/Randomizer/Location.cs
+++ b/Randomizer/Location.cs
@@ -173,6 +173,18 @@ public Item GetItemContents()
public bool CanPlace(Item itemToPlace, List
- availableItems, List locations)
{
+ switch (Type)
+ {
+ case LocationType.Helper:
+ case LocationType.Untyped:
+ return false;
+ }
+
+ if (Address == 0)
+ {
+ return false;
+ }
+
Console.WriteLine($"Evaluating: {Name}");
foreach (Dependency dependency in Dependencies)
{
diff --git a/Randomizer/Shuffler.cs b/Randomizer/Shuffler.cs
index bbf2c05b..9df83d40 100644
--- a/Randomizer/Shuffler.cs
+++ b/Randomizer/Shuffler.cs
@@ -121,7 +121,7 @@ public void LoadLocations(string locationFile)
public void RandomizeLocations()
{
List
- unplacedItems = MajorItems.ToList();
- List unfilledLocations = Locations.Where(location => !location.Filled && location.Type != Location.LocationType.Helper).ToList();
+ List unfilledLocations = Locations.Where(location => !location.Filled).ToList();
unfilledLocations.Shuffle(RNG);
unplacedItems.Shuffle(RNG);
@@ -149,14 +149,13 @@ public void RandomizeLocations()
private void FillLocations(List
- items, List locations)
{
- int itemIndex;
for (int i = items.Count - 1; i >= 0; i--)
{
- itemIndex = RNG.Next(items.Count);
+ int itemIndex = RNG.Next(items.Count);
Item item = items[itemIndex];
Console.WriteLine($"Placing: {item.Type.ToString()}");
items.RemoveAt(itemIndex);
- List availableLocations = locations.Where(location => location.CanPlace(item, items, locations)).ToList();
+ List availableLocations = locations.Where(location => location.CanPlace(item, items, Locations)).ToList();
if (availableLocations.Count <= 0)
{
@@ -164,9 +163,11 @@ private void FillLocations(List
- items, List locations)
return;
}
- availableLocations[0].Fill(item);
- Console.WriteLine($"Placed {item.Type.ToString()} at {availableLocations[0].Name} with {items.Count} items remaining\n");
- locations.Remove(availableLocations[0]);
+ int locationIndex = RNG.Next(availableLocations.Count);
+
+ availableLocations[locationIndex].Fill(item);
+ Console.WriteLine($"Placed {item.Type.ToString()} at {availableLocations[locationIndex].Name} with {items.Count} items remaining\n");
+ locations.Remove(availableLocations[locationIndex]);
}
}
diff --git a/Resources/default.logic b/Resources/default.logic
index 7516a489..fd526341 100644
--- a/Resources/default.logic
+++ b/Resources/default.logic
@@ -10,17 +10,35 @@
# Items.(itemName)[.subValue] requires the given item for the location
# Locations.(locationName) requires the location to be accessible first
+# Item Macro Helpers
+HasSword;Helper;0; (|Items.SmithSword, Items.GreenSword, Items.BlueSword, Items.FourSword)
+HasBottle;Helper;0; (|Items.Bottle1, Items.Bottle2, Items.Bottle3, Items.Bottle4)
+
+# Misc Locations
SmithHouse;Minor;22-11-00
+SmithSword;Normal;009CC8;;Items.SmithSword
+BrokenPicori;Normal;008F8A;;Items.Shield
+SouthKeeseCave;Minor;32-13-00; Items.BombBag
+BelariBombs;Normal;00A00C; (|Items.BombBag, Locations.CompleteDeepwood)
+
+AccessCrenel;Helper;0; Items.BombBag, Items.GustJar, Locations.HasBottle
+
+
+SaveEzlo;Helper;0;
+#JabberNut;Normal;;Locations.SaveEzlo
+
+BottleDeku;Normal;0CC0C0; Items.Shield, Items.BombBag, Locations.HasSword, Items.SpinAttack
# Deepwood locations
-# Need gust jar to leave room
-DeepwoodWiggler;Normal;48-00-01;Items.GustJar
-DeepwoodButtonShells1;Minor;48-01-01;Items.GustJar
-DeepwoodButtonShells2;Minor;48-01-02;Items.GustJar
-DeepwoodPreCompassShells;Minor;48-02-01;Items.GustJar
-DeepwoodBarrelShells;Minor;48-06-01;Items.GustJar
-DeepwoodUpstairsChest;Minor;48-17-01;
+DeepwoodAccess;Helper;0;
+# Need gust jar or lantern to leave room
+DeepwoodWiggler;Normal;48-00-01; Locations.DeepwoodAccess, Locations.HasSword, (|Items.GustJar, Items.LanternOff)
+DeepwoodButtonShells1;Minor;48-01-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag)
+DeepwoodButtonShells2;Minor;48-01-02; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag)
+DeepwoodPreCompassShells;Minor;48-02-01; Locations.DeepwoodAccess,(|Items.GustJar, Items.BombBag)
+DeepwoodBarrelShells;Minor;48-06-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag)
+DeepwoodUpstairsChest;Minor;48-17-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.LanternOff)
+CompleteDeepwood;Helper;0; Items.GustJar, Locations.HasSword
# Cave of Flames locations
-CoFAccess;Helper;0;
-CoFBigChest
\ No newline at end of file
+CoFAccess;Helper;0; Locations.AccessCrenel
From 7fbe2e3eedd310b77ababbf1fd42a9e706a23afb Mon Sep 17 00:00:00 2001
From: 21aslade <21aslade@go.dsdmail.net>
Date: Sat, 15 Jun 2019 20:48:24 -0600
Subject: [PATCH 06/41] Fix helper bug, add Jabber Nut and new chests to logic
---
Randomizer/Dependency.cs | 6 +++---
Randomizer/Location.cs | 36 ++++++++++++++++++++++++++----------
Randomizer/Shuffler.cs | 18 ++++++++++++++++++
Resources/default.logic | 22 +++++++++++-----------
4 files changed, 58 insertions(+), 24 deletions(-)
diff --git a/Randomizer/Dependency.cs b/Randomizer/Dependency.cs
index adb9e378..fb5f2c5f 100644
--- a/Randomizer/Dependency.cs
+++ b/Randomizer/Dependency.cs
@@ -49,6 +49,7 @@ public static List GetDependencies(string logic)
switch (dependencyParts[0])
{
case "Locations":
+ case "Helpers":
LocationDependency locationDependency = new LocationDependency(dependencyParts[1]);
dependencies.Add(locationDependency);
break;
@@ -98,9 +99,9 @@ public override bool DependencyFulfilled(List
- availableItems, List availableItems, List de
Filled = false;
}
- public void WriteLocation(Writer r)
+ public void WriteLocation(Writer w)
{
+ if (Address == 0)
+ {
+ return;
+ }
+
switch (Type)
{
case LocationType.Helper:
case LocationType.Untyped:
return;
+ case LocationType.JabberNonsense:
+ w.WriteByte((byte)Contents.Type, Address);
+ w.WriteByte(Contents.SubValue, Address + 2);
+ break;
+ case LocationType.Normal:
+ case LocationType.Minor:
+ default:
+
+ w.SetPosition(Address);
+ w.WriteByte((byte)Contents.Type);
+ w.WriteByte(Contents.SubValue);
+ break;
}
-
- if (Address == 0)
- {
- return;
- }
-
- r.SetPosition(Address);
- r.WriteByte((byte)Contents.Type);
- r.WriteByte(Contents.SubValue);
}
public Item GetItemContents()
@@ -185,15 +194,22 @@ public bool CanPlace(Item itemToPlace, List
- availableItems, List
return false;
}
+ return IsAccessible(availableItems, locations);
+ }
+
+ public bool IsAccessible(List
- availableItems, List locations)
+ {
Console.WriteLine($"Evaluating: {Name}");
foreach (Dependency dependency in Dependencies)
{
if (!dependency.DependencyFulfilled(availableItems, locations))
{
+ Console.WriteLine("Unavailable :(");
return false;
}
}
+ Console.WriteLine("Available!");
return true;
}
diff --git a/Randomizer/Shuffler.cs b/Randomizer/Shuffler.cs
index 9df83d40..ad817259 100644
--- a/Randomizer/Shuffler.cs
+++ b/Randomizer/Shuffler.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
@@ -110,6 +111,7 @@ public void LoadLocations(string locationFile)
MinorItems.Add(newLocation.Contents);
break;
case Location.LocationType.Normal:
+ case Location.LocationType.JabberNonsense:
default:
Console.WriteLine($"Hey! {newLocation.Contents.Type.ToString()}");
MajorItems.Add(newLocation.Contents);
@@ -120,6 +122,9 @@ public void LoadLocations(string locationFile)
public void RandomizeLocations()
{
+ //File.WriteAllBytes(OutputDirectory + "/mcrando.gba", ROM.Instance.romData); // Write file for patching. Bad, fix later
+ //ApplyPatch(OutputDirectory + "/mcrando.gba");
+
List
- unplacedItems = MajorItems.ToList();
List unfilledLocations = Locations.Where(location => !location.Filled).ToList();
unfilledLocations.Shuffle(RNG);
@@ -179,5 +184,18 @@ private void FastFillLocations(List
- items, List locations)
locations.RemoveAt(0);
}
}
+
+ /*private void ApplyPatch(string outputPath)
+ {
+ string directory = Directory.GetCurrentDirectory();
+
+ using (Process process = new Process())
+ {
+ process.StartInfo.FileName = "ups.exe";
+ process.StartInfo.Arguments = $"patch -b {ROM.Instance.path} -p {directory + "/randopatch.ups"} -o {outputPath}";
+ process.Start();
+ process.WaitForExit();
+ }
+ }*/
}
}
diff --git a/Resources/default.logic b/Resources/default.logic
index fd526341..fdf20fde 100644
--- a/Resources/default.logic
+++ b/Resources/default.logic
@@ -16,29 +16,29 @@ HasBottle;Helper;0; (|Items.Bottle1, Items.Bottle2, Items.Bottle3, Items.Bottle4
# Misc Locations
SmithHouse;Minor;22-11-00
-SmithSword;Normal;009CC8;;Items.SmithSword
-BrokenPicori;Normal;008F8A;;Items.Shield
+Intro1;Normal;22-11-01
+Intro2;Normal;22-11-02
+Intro3;Normal;22-11-03
SouthKeeseCave;Minor;32-13-00; Items.BombBag
-BelariBombs;Normal;00A00C; (|Items.BombBag, Locations.CompleteDeepwood)
+BelariBombs;Normal;00A00C
-AccessCrenel;Helper;0; Items.BombBag, Items.GustJar, Locations.HasBottle
+AccessCrenel;Helper;0; Items.BombBag, Items.GustJar, Helpers.HasBottle
+JabberNut;JabberNonsense;09498C
-SaveEzlo;Helper;0;
-#JabberNut;Normal;;Locations.SaveEzlo
-
-BottleDeku;Normal;0CC0C0; Items.Shield, Items.BombBag, Locations.HasSword, Items.SpinAttack
+BottleDeku;Normal;0CC0C0; Items.Shield, Items.BombBag, Helpers.HasSword
+#, Items.SpinAttack
# Deepwood locations
-DeepwoodAccess;Helper;0;
+DeepwoodAccess;Helper;0; Items.JabberNut
# Need gust jar or lantern to leave room
-DeepwoodWiggler;Normal;48-00-01; Locations.DeepwoodAccess, Locations.HasSword, (|Items.GustJar, Items.LanternOff)
+DeepwoodWiggler;Normal;48-00-01; Locations.DeepwoodAccess, Helpers.HasSword, (|Items.GustJar, Items.LanternOff)
DeepwoodButtonShells1;Minor;48-01-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag)
DeepwoodButtonShells2;Minor;48-01-02; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag)
DeepwoodPreCompassShells;Minor;48-02-01; Locations.DeepwoodAccess,(|Items.GustJar, Items.BombBag)
DeepwoodBarrelShells;Minor;48-06-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag)
DeepwoodUpstairsChest;Minor;48-17-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.LanternOff)
-CompleteDeepwood;Helper;0; Items.GustJar, Locations.HasSword
+CompleteDeepwood;Helper;0; Locations.DeepwoodAccess, Items.GustJar, Locations.HasSword
# Cave of Flames locations
CoFAccess;Helper;0; Locations.AccessCrenel
From 7b22194730a68e1bc305ca4c76981696ad5b2f48 Mon Sep 17 00:00:00 2001
From: 21aslade <21aslade@go.dsdmail.net>
Date: Mon, 17 Jun 2019 22:38:20 -0600
Subject: [PATCH 07/41] Add logic for Mount Crenel locations
---
Randomizer/Dependency.cs | 2 --
Randomizer/Location.cs | 28 +++++++++++++++++---
Randomizer/Shuffler.cs | 37 ++++++++++++++++++++++-----
Resources/default.logic | 55 ++++++++++++++++++++++------------------
4 files changed, 87 insertions(+), 35 deletions(-)
diff --git a/Randomizer/Dependency.cs b/Randomizer/Dependency.cs
index fb5f2c5f..a7331409 100644
--- a/Randomizer/Dependency.cs
+++ b/Randomizer/Dependency.cs
@@ -20,8 +20,6 @@ public static List GetDependencies(string logic)
{
List dependencies = new List();
- logic = logic.Replace(" ", "");
-
// Match: comma, capture and match anything between
string regexPattern = @"(?:,|^)\(([&|](?:[^()]|(?
\()|(?<-p>\)))+)\)(?:,|$)|,";
string[] subLogic = Regex.Split(logic, regexPattern);
diff --git a/Randomizer/Location.cs b/Randomizer/Location.cs
index b19085a2..4a2e8f13 100644
--- a/Randomizer/Location.cs
+++ b/Randomizer/Location.cs
@@ -19,6 +19,7 @@ public static Location GetLocation(string locationText)
if (locationParts.Length < 3)
{
+ Console.WriteLine("Too short");
return new Location(LocationType.Untyped, "INVALID LOCATION", 0, null);
}
@@ -27,6 +28,7 @@ public static Location GetLocation(string locationText)
string locationType = locationParts[1];
if (!Enum.TryParse(locationType, out LocationType type) || type == LocationType.Untyped)
{
+ Console.WriteLine("Invalid type");
return new Location(LocationType.Untyped, "INVALID LOCATION", 0, null);
}
@@ -73,6 +75,11 @@ public static Location GetLocation(string locationText)
public static int GetAddressFromString(string addressString)
{
// Either direct address or area-room-chest
+ if (addressString == "")
+ {
+ return 0;
+ }
+
if (int.TryParse(addressString, NumberStyles.HexNumber, null, out int address))
{
return address;
@@ -116,7 +123,8 @@ public enum LocationType
KinstoneItem,
HeartPieceItem,
JabberNonsense,
- Helper
+ Helper,
+ Starting
}
public List Dependencies;
@@ -174,8 +182,22 @@ public void WriteLocation(Writer w)
public Item GetItemContents()
{
- ItemType type = (ItemType)ROM.Instance.reader.ReadByte(Address);
- byte subType = ROM.Instance.reader.ReadByte();
+ ItemType type = ItemType.Untyped;
+ byte subType = 0;
+
+ switch (Type)
+ {
+ case LocationType.JabberNonsense:
+ type = (ItemType)ROM.Instance.reader.ReadByte(Address);
+ subType = ROM.Instance.reader.ReadByte(Address + 2);
+ break;
+ default:
+ type = (ItemType)ROM.Instance.reader.ReadByte(Address);
+ subType = ROM.Instance.reader.ReadByte();
+ break;
+ }
+
+
return new Item(type, subType);
}
diff --git a/Randomizer/Shuffler.cs b/Randomizer/Shuffler.cs
index ad817259..ae69bf3f 100644
--- a/Randomizer/Shuffler.cs
+++ b/Randomizer/Shuffler.cs
@@ -52,6 +52,7 @@ public class Shuffler
private Random RNG;
private List Locations;
+ private List StartingLocations;
private List- MajorItems;
private List
- MinorItems;
private string OutputDirectory;
@@ -93,25 +94,40 @@ public void LoadLocations(string locationFile)
locationStrings = File.ReadAllLines(locationFile);
}
- foreach (string locationString in locationStrings)
+ foreach (string locationLine in locationStrings)
{
- if (locationString == "" || locationString[0] == '#')
+ string locationString = locationLine.Split('#')[0].Replace(" ", "");
+ locationString = locationString.Replace(" ", "");
+ if (locationString == "")
{
continue;
}
Location newLocation = Location.GetLocation(locationString);
- Locations.Add(newLocation);
+
+ switch (newLocation.Type)
+ {
+ case Location.LocationType.Starting:
+ StartingLocations.Add(newLocation);
+ break;
+ default:
+ Locations.Add(newLocation);
+ break;
+ }
+
switch(newLocation.Type)
{
case Location.LocationType.Untyped:
case Location.LocationType.Helper:
+ Console.WriteLine($"Helper or untyped {newLocation.Name}");
break;
case Location.LocationType.Minor:
+ Console.WriteLine(newLocation.Contents.Type.ToString());
MinorItems.Add(newLocation.Contents);
break;
case Location.LocationType.Normal:
case Location.LocationType.JabberNonsense:
+ case Location.LocationType.Starting:
default:
Console.WriteLine($"Hey! {newLocation.Contents.Type.ToString()}");
MajorItems.Add(newLocation.Contents);
@@ -122,14 +138,19 @@ public void LoadLocations(string locationFile)
public void RandomizeLocations()
{
- //File.WriteAllBytes(OutputDirectory + "/mcrando.gba", ROM.Instance.romData); // Write file for patching. Bad, fix later
- //ApplyPatch(OutputDirectory + "/mcrando.gba");
+ List
- allItems = MajorItems.Concat(MinorItems).ToList();
+
List
- unplacedItems = MajorItems.ToList();
- List unfilledLocations = Locations.Where(location => !location.Filled).ToList();
+ List unfilledLocations = Locations.Where(location => !location.Filled && location.Type != Location.LocationType.Helper && location.Type != Location.LocationType.Untyped).ToList();
unfilledLocations.Shuffle(RNG);
unplacedItems.Shuffle(RNG);
+ foreach (Item item in MajorItems)
+ {
+ Console.WriteLine($"Item: {item.Type.ToString()} Sub: {item.SubValue}");
+ }
+
FillLocations(MajorItems.ToList(), unfilledLocations);
unfilledLocations.Shuffle(RNG);
@@ -159,6 +180,10 @@ private void FillLocations(List
- items, List locations)
int itemIndex = RNG.Next(items.Count);
Item item = items[itemIndex];
Console.WriteLine($"Placing: {item.Type.ToString()}");
+ if (item.Type == ItemType.KinstoneX || item.Type == ItemType.ShellsX || item.Type == ItemType.Rupee20)
+ {
+ Console.WriteLine("Inconcievable!");
+ }
items.RemoveAt(itemIndex);
List availableLocations = locations.Where(location => location.CanPlace(item, items, Locations)).ToList();
diff --git a/Resources/default.logic b/Resources/default.logic
index fdf20fde..c08b4aba 100644
--- a/Resources/default.logic
+++ b/Resources/default.logic
@@ -9,36 +9,43 @@
# (&a,b,c...) and (|a,b,c...) are an and/or of the contents
# Items.(itemName)[.subValue] requires the given item for the location
# Locations.(locationName) requires the location to be accessible first
+# Helpers.(helperName) is equivalent for Locations.(locationName), only provided for readability
+# Item Override should be in the form of Items.(itemName)[.subValue]
+
+# Beginning Items - Only item override is set to introduce them to the pool
+#Intro1; Starting;;; Items.Shield
+#Intro2; Starting;;; Items.SmithSword
# Item Macro Helpers
-HasSword;Helper;0; (|Items.SmithSword, Items.GreenSword, Items.BlueSword, Items.FourSword)
-HasBottle;Helper;0; (|Items.Bottle1, Items.Bottle2, Items.Bottle3, Items.Bottle4)
+HasSword; Helper;; (|Items.SmithSword, Items.GreenSword, Items.ReadSword, Items.BlueSword, Items.FourSword)
+HasBottle; Helper;; (|Items.Bottle1.FF, Items.Bottle2.FF, Items.Bottle3.FF, Items.Bottle4.FF)
# Misc Locations
-SmithHouse;Minor;22-11-00
-Intro1;Normal;22-11-01
+SmithHouse; Minor; 22-11-00
+Intro1;Normal;22-11-01 # Will remove these later
Intro2;Normal;22-11-02
-Intro3;Normal;22-11-03
-SouthKeeseCave;Minor;32-13-00; Items.BombBag
-BelariBombs;Normal;00A00C
-
-AccessCrenel;Helper;0; Items.BombBag, Items.GustJar, Helpers.HasBottle
-
-JabberNut;JabberNonsense;09498C
-
-BottleDeku;Normal;0CC0C0; Items.Shield, Items.BombBag, Helpers.HasSword
-#, Items.SpinAttack
+SouthKeeseCave; Minor; 32-13-00; Items.BombBag
+BelariBombs; Normal; 00A00C
+JabberNut; JabberNonsense; 09498C
+BottleScrub; Normal; 0CC0C0; Items.Shield, Items.BombBag, Helpers.HasSword #, Items.SpinAttack
+
+# Mount Crenel Locations
+AccessCrenel;Helper;; Helpers.HasBottle, Items.BombBag
+CrenelLowerScrub; Minor; 0CC09C; Helpers.HasBottle, Items.Shield # Technically a Crenel item, but doesn't need bombs
+CrenelMinishHole; Minor; 35-00-00; Locations.AccessCrenel
+CrenelCaveDownstairs; Minor; 26-07-00; Locations.AccessCrenel
+CrenelGripScrub; Normal; 0CC0A8; Locations.AccessCrenel, Items.Shield
+CrenelBlockChest; Minor; 26-03-00; Locations.AccessCrenel, (|Items.PacciCane, (&Items.GustJar, Items.GripRing.FF))
# Deepwood locations
-DeepwoodAccess;Helper;0; Items.JabberNut
-# Need gust jar or lantern to leave room
-DeepwoodWiggler;Normal;48-00-01; Locations.DeepwoodAccess, Helpers.HasSword, (|Items.GustJar, Items.LanternOff)
-DeepwoodButtonShells1;Minor;48-01-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag)
-DeepwoodButtonShells2;Minor;48-01-02; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag)
-DeepwoodPreCompassShells;Minor;48-02-01; Locations.DeepwoodAccess,(|Items.GustJar, Items.BombBag)
-DeepwoodBarrelShells;Minor;48-06-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag)
-DeepwoodUpstairsChest;Minor;48-17-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.LanternOff)
-CompleteDeepwood;Helper;0; Locations.DeepwoodAccess, Items.GustJar, Locations.HasSword
+DeepwoodAccess;Helper;; Items.JabberNut
+DeepwoodWiggler; Normal; 48-00-01; Locations.DeepwoodAccess, Helpers.HasSword, (|Items.GustJar, Items.LanternOff) # Need gust jar or lantern to leave room
+DeepwoodButtonShells1; Minor; 48-01-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag)
+DeepwoodButtonShells2; Minor; 48-01-02; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag)
+DeepwoodPreCompassShells; Minor; 48-02-01; Locations.DeepwoodAccess,(|Items.GustJar, Items.BombBag)
+DeepwoodBarrelShells; Minor; 48-06-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag)
+DeepwoodUpstairsChest; Minor; 48-17-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.LanternOff)
+CompleteDeepwood; Helper;; Locations.DeepwoodAccess, Items.GustJar, Locations.HasSword
# Cave of Flames locations
-CoFAccess;Helper;0; Locations.AccessCrenel
+CoFAccess;Helper;; Locations.AccessCrenel, (|Items.PacciCane, (&Items.GustJar, Items.GripRing.FF))
From e5a0315b4acb6d22929e344ff29364ab636a1f04 Mon Sep 17 00:00:00 2001
From: 21aslade <21aslade@go.dsdmail.net>
Date: Wed, 19 Jun 2019 17:37:31 -0600
Subject: [PATCH 08/41] Add capability to apply UPS patches
---
Core/ROM.cs | 4 +-
MinishRandomizer.csproj | 1 +
Utilities/PatchUtil.cs | 153 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 156 insertions(+), 2 deletions(-)
create mode 100644 Utilities/PatchUtil.cs
diff --git a/Core/ROM.cs b/Core/ROM.cs
index 7ce16a92..7d8f937f 100644
--- a/Core/ROM.cs
+++ b/Core/ROM.cs
@@ -22,13 +22,13 @@ public ROM(string filePath)
Instance = this;
path = filePath;
byte[] smallData = File.ReadAllBytes(filePath);
- if (smallData.Length >= 0x01000000)
+ if (smallData.Length >= 0x02000000)
{
romData = smallData;
}
else
{
- romData = new byte[0x1000000];
+ romData = new byte[0x2000000];
smallData.CopyTo(romData, 0);
}
diff --git a/MinishRandomizer.csproj b/MinishRandomizer.csproj
index 517ea6c9..95009418 100644
--- a/MinishRandomizer.csproj
+++ b/MinishRandomizer.csproj
@@ -59,6 +59,7 @@
+
diff --git a/Utilities/PatchUtil.cs b/Utilities/PatchUtil.cs
new file mode 100644
index 00000000..f370f625
--- /dev/null
+++ b/Utilities/PatchUtil.cs
@@ -0,0 +1,153 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MinishRandomizer.Utilities
+{
+ static class PatchUtil {
+ ///
+ /// Apply a UPS patch to a byte array, in place
+ ///
+ /// The data to patch
+ /// The patch to apply
+ public static void ApplyUPS(byte[] inputData, byte[] patchData, bool checkCRC = true)
+ {
+ using (MemoryStream inputStream = new MemoryStream(inputData))
+ {
+ Reader dataReader = new Reader(inputStream);
+ Writer dataWriter = new Writer(inputStream);
+
+ using (MemoryStream patchStream = new MemoryStream(patchData))
+ {
+ Reader patchReader = new Reader(patchStream);
+
+ patchReader.SetPosition(patchData.Length - 12);
+ uint inputCRC = patchReader.ReadUInt32();
+ uint outputCRC = patchReader.ReadUInt32();
+ uint patchCRC = patchReader.ReadUInt32();
+
+ patchReader.SetPosition(0);
+
+ // Check for magic number
+ if (!Enumerable.SequenceEqual(patchReader.ReadBytes(4), new byte[] { 0x55, 0x50, 0x53, 0x31 }))
+ {
+ Console.WriteLine("Invalid patch: Lacks magic number!");
+ return;
+ }
+
+ long inputSize = ReadVWI(patchReader);
+ long outputSize = ReadVWI(patchReader);
+
+ if (outputSize > inputData.Length)
+ {
+ Console.WriteLine("Problem: Can't change ROM size while patching in this implementation.");
+ return;
+ }
+
+ if (checkCRC && crc32(inputData, inputSize) != inputCRC)
+ {
+ Console.WriteLine("Input ROM has an invalid CRC!");
+ return;
+ }
+
+ if (checkCRC && crc32(patchData, patchData.Length - 4) != patchCRC)
+ {
+ Console.WriteLine("Patch has an invalid CRC!");
+ return;
+ }
+
+ dataReader.SetPosition(0);
+ dataWriter.SetPosition(0);
+
+ while (patchReader.Position < patchData.Length - 12)
+ {
+ long skippedBytes = ReadVWI(patchReader);
+ dataReader.SetPosition(dataReader.Position + skippedBytes);
+
+ byte nextPatch = patchReader.ReadByte();
+ while (nextPatch != 0)
+ {
+ byte nextInput = dataReader.PeekByte();
+
+ dataWriter.WriteByte((byte)(nextInput ^ nextPatch));
+
+ nextPatch = patchReader.ReadByte();
+ }
+
+ dataReader.ReadByte(); // Advance the read/write head one byte
+ }
+
+ if (crc32(inputData, outputSize) != outputCRC)
+ {
+ Console.WriteLine("Output ROM has an invalid CRC! ...but the array was already patched!");
+ return;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Read a variable-width integer in UPS and BPS format, adapted from https://www.romhacking.net/patch/crc32
+ ///
+ /// The reader to read the integer from
+ /// The value of the integer, restricted to being a 64-bit signed integer
+ private static long ReadVWI(Reader r)
+ {
+ byte nextByte;
+ long output = 0;
+ int shift = 1;
+ do
+ {
+ nextByte = r.ReadByte();
+
+ output += (nextByte & 0x7F) * shift;
+
+ shift <<= 7;
+ output += shift;
+ }
+ while ((nextByte & 0x80) == 0);
+
+ output -= shift;
+
+ return output;
+ }
+
+ private static void WriteVWI(Writer w)
+ {
+
+ }
+
+ ///
+ /// Find the crc32 of a byte array, adapted from https://www.romhacking.net/patch/crc32
+ ///
+ /// The byte array to checksum
+ /// The size of the array to checksum, negative values equal the size of data
+ private static uint crc32(byte[] data, long length)
+ {
+ uint[] crcTable = new uint[256];
+
+ for (int i = 0; i < 256; i++)
+ {
+ uint c = (uint)i;
+
+ for (int j = 0; j < 8; j++)
+ {
+ c = ((c & 1) >= 1 ? (0xedb88320 ^ (c >> 1)) : (c >> 1));
+ }
+ crcTable[i] = c;
+ }
+
+ uint outputCRC = 0xFFFFFFFF;
+ for (int i = 0; i < length; i++)
+ {
+ //Console.WriteLine(StringUtil.AsStringHex8((int)outputCRC));
+ outputCRC = (outputCRC >> 8) ^ crcTable[(outputCRC ^ data[i]) & 0xFF];
+ }
+
+ return outputCRC ^ 0xFFFFFFFF;
+ }
+ }
+}
From 95f1498bcda83edf6bca46259563a29ac6e054c7 Mon Sep 17 00:00:00 2001
From: 21aslade <21aslade@go.dsdmail.net>
Date: Thu, 20 Jun 2019 19:26:10 -0600
Subject: [PATCH 09/41] Add default UPS patch to open the world, update logic
---
MinishRandomizer.csproj | 1 +
Randomizer/Location.cs | 10 ++++-
Randomizer/Shuffler.cs | 24 +++++++++++-
Resources/default.logic | 29 ++++++++-------
Resources/randoPatch.ups | Bin 0 -> 471 bytes
UI/MainWindow.Designer.cs | 75 +++++++++++++++++++++++++++++---------
UI/MainWindow.cs | 31 ++++++++++++++++
7 files changed, 136 insertions(+), 34 deletions(-)
create mode 100644 Resources/randoPatch.ups
diff --git a/MinishRandomizer.csproj b/MinishRandomizer.csproj
index 95009418..524c2d5e 100644
--- a/MinishRandomizer.csproj
+++ b/MinishRandomizer.csproj
@@ -86,6 +86,7 @@
True
+