diff --git a/Assets/Scripts/Attribute.cs b/Assets/Scripts/Attribute.cs
new file mode 100644
index 0000000..c8c9090
--- /dev/null
+++ b/Assets/Scripts/Attribute.cs
@@ -0,0 +1,42 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+public struct Attribute
+{
+ [SerializeField]
+ private long _base;
+
+ public Attribute(long stat)
+ {
+ _base = stat;
+ }
+
+ ///
+ /// Permanently increase the stat value.
+ ///
+ /// Increase amount.
+ public void LevelUp(long by)
+ {
+ _base += by;
+ }
+
+ ///
+ /// Calculates the stat value with accumulated buff and debuff modifiers.
+ ///
+ /// Additive bonus applied before multiplicative scaling.
+ /// Multiplicative scaling in hundredths of a percent. E.g., 625 permyriad corresponds to 6.25%.
+ /// Additive bonus applied after multiplicative scaling.
+ /// (base + baseBonus) * (100% + permyriadBonus) + flatBonus, bounded below by 0.
+ public long Calc(long baseBonus, long permyriadBonus, long flatBonus)
+ {
+ var r = _base + baseBonus;
+ var m = permyriadBonus + 10000;
+ r = r * (m / 10000) + (r / 10000) * (m % 10000) + flatBonus;
+ if (r <= 0)
+ {
+ return 0;
+ }
+ return r;
+ }
+}
diff --git a/Assets/Scripts/Attribute.cs.meta b/Assets/Scripts/Attribute.cs.meta
new file mode 100644
index 0000000..b99cb46
--- /dev/null
+++ b/Assets/Scripts/Attribute.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b7d084c6ff925d3448dad326e9980f08
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/IdleSurvivors.asmdef b/Assets/Scripts/IdleSurvivors.asmdef
new file mode 100644
index 0000000..89fe15a
--- /dev/null
+++ b/Assets/Scripts/IdleSurvivors.asmdef
@@ -0,0 +1,3 @@
+{
+ "name": "IdleSurvivors"
+}
diff --git a/Assets/Scripts/IdleSurvivors.asmdef.meta b/Assets/Scripts/IdleSurvivors.asmdef.meta
new file mode 100644
index 0000000..498fd2f
--- /dev/null
+++ b/Assets/Scripts/IdleSurvivors.asmdef.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: e5250715ffed2b644bacb4c76ae6a775
+AssemblyDefinitionImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Tests.meta b/Assets/Tests.meta
new file mode 100644
index 0000000..ffccc17
--- /dev/null
+++ b/Assets/Tests.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 9048b33fed80c814dbfdb2713db19d6d
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Tests/AttributeTest.cs b/Assets/Tests/AttributeTest.cs
new file mode 100644
index 0000000..8668e08
--- /dev/null
+++ b/Assets/Tests/AttributeTest.cs
@@ -0,0 +1,25 @@
+using System.Collections;
+using System.Collections.Generic;
+using NUnit.Framework;
+using UnityEngine;
+using UnityEngine.TestTools;
+
+public class AttributeTest
+{
+ [Test]
+ [TestCase(1000000, 0, 0, 0, 0, ExpectedResult = 1000000)]
+ [TestCase(1000000, 1, 0, 0, 0, ExpectedResult = 1000001)]
+ [TestCase(1000000, 0, 2, 0, 0, ExpectedResult = 1000002)]
+ [TestCase(1000000, 0, 0, 10000, 0, ExpectedResult = 2000000)]
+ [TestCase(1000000, 0, 0, 0, 3, ExpectedResult = 1000003)]
+ [TestCase(0, 1000000, 1000000, 5000, 1000, ExpectedResult = 3001000)]
+ [TestCase(1000000, 0, 0, -10000, 0, ExpectedResult = 0)]
+ [TestCase(1000000, 0, 0, -20000, 0, ExpectedResult = 0)]
+ [TestCase(1000000, 0, 0, -20000, 50, ExpectedResult = 0)]
+ public long Calc(long startStat, long levelUp, long baseBonus, long permyriadBonus, long flatBonus)
+ {
+ var attr = new Attribute(startStat);
+ attr.LevelUp(levelUp);
+ return attr.Calc(baseBonus, permyriadBonus, flatBonus);
+ }
+}
diff --git a/Assets/Tests/AttributeTest.cs.meta b/Assets/Tests/AttributeTest.cs.meta
new file mode 100644
index 0000000..2139830
--- /dev/null
+++ b/Assets/Tests/AttributeTest.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d4d405f62d1e7dc4aaf451051bf27d73
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Tests/Tests.asmdef b/Assets/Tests/Tests.asmdef
new file mode 100644
index 0000000..3bafd34
--- /dev/null
+++ b/Assets/Tests/Tests.asmdef
@@ -0,0 +1,24 @@
+{
+ "name": "Tests",
+ "rootNamespace": "",
+ "references": [
+ "UnityEngine.TestRunner",
+ "UnityEditor.TestRunner",
+ "IdleSurvivors"
+ ],
+ "includePlatforms": [
+ "Editor"
+ ],
+ "excludePlatforms": [],
+ "allowUnsafeCode": false,
+ "overrideReferences": true,
+ "precompiledReferences": [
+ "nunit.framework.dll"
+ ],
+ "autoReferenced": false,
+ "defineConstraints": [
+ "UNITY_INCLUDE_TESTS"
+ ],
+ "versionDefines": [],
+ "noEngineReferences": false
+}
\ No newline at end of file
diff --git a/Assets/Tests/Tests.asmdef.meta b/Assets/Tests/Tests.asmdef.meta
new file mode 100644
index 0000000..945fa88
--- /dev/null
+++ b/Assets/Tests/Tests.asmdef.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 0e24b24d5798c7c4b979b314b1a7afce
+AssemblyDefinitionImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant: