Browse Source

feat: add curses version of simple_dice
refactor: add sides to result object in order to color best/worst

Sam Jaffe 1 year ago
parent
commit
0910a70145

+ 4 - 0
.gitmodules

@@ -7,3 +7,7 @@
 [submodule "external/shared_random_generator"]
 	path = external/shared_random_generator
 	url = ssh://git@gogs.sjaffe.name:3000/sjjaffe/cpp-shared-random-generator.git
+[submodule "external/ncurses-wrapper"]
+	path = external/ncurses-wrapper
+	url = ssh://git@gogs.sjaffe.name:3000/sjjaffe/cpp-ncurses-wrapper.git
+	branch = main

+ 95 - 0
curses_dice/main.cxx

@@ -0,0 +1,95 @@
+//
+//  main.cxx
+//  curses_dice
+//
+//  Created by Sam Jaffe on 7/23/24.
+//  Copyright © 2024 Sam Jaffe. All rights reserved.
+//
+
+#include <sstream>
+
+#include <ncurses-wrapper/cli.h>
+#include <ncurses-wrapper/color.h>
+#include <ncurses-wrapper/window.h>
+
+#include "dice-roll/die.h"
+#include "dice-roll/exception.h"
+#include "dice-roll/roll.h"
+
+using namespace curses;
+
+ColorPair color(bool good, bool bad) {
+  if (good) {
+    return ColorPair{Color::GREEN, Color::DEFAULT};
+  } else if (bad) {
+    return ColorPair{Color::RED, Color::DEFAULT};
+  } else {
+    return ColorPair{Color::DEFAULT, Color::DEFAULT};
+  }
+}
+
+ColorPair color(bool good) { return color(good, !good); }
+
+void print(curses::Window & window, int roll, int sides) {
+  if (auto scope = window.with(color(roll == sides, roll == 1))) {
+    window.printf("%d", roll);
+  }
+}
+
+void print(curses::Window & window, dice::dice_roll const & r) {
+  if (r.dc.comp != dice::difficulty_class::test::None) {
+    if (auto scope = window.with(color(int(r)))) {
+      window << (int(r) ? "PASS" : "FAIL");
+    }
+    window.printf(" (");
+  } else {
+    window.printf("%d (", int(r));
+  }
+  for (dice::die_roll const & dr : r.sub_rolls) {
+    window << dr.sign;
+    switch (dr.rolled.size()) {
+    case 0:
+      window.printf("0");
+      break;
+    case 1:
+      print(window, dr.rolled[0], dr.sides);
+      break;
+    default:
+      window.printf("[ ");
+      print(window, dr.rolled[0], dr.sides);
+      for (int i = 1; i < dr.rolled.size(); ++i) {
+        window.printf(", ");
+        print(window, dr.rolled[i], dr.sides);
+      }
+      window.printf(" ]");
+    }
+  }
+  for (dice::mod const & m : r.modifiers) {
+    window << m.sign;
+    window.printf("%d", m.value);
+  }
+  window.printf(")\n");
+}
+
+void print(curses::Window & window, std::vector<dice::dice_roll> const & rs) {
+  if (rs.size() != 1) {
+    window.printf("\n");
+    for (int i = 0; i < rs.size(); ++i) {
+      window.printf("  Result/%d: ", i);
+      print(window, rs[i]);
+    }
+  } else {
+    print(window, rs[0]);
+  }
+}
+
+int main(int, const char **) {
+  Cli("> ", WithColor).loop([](curses::Window & window, std::string line) {
+    if (line.empty()) return;
+    auto d = dice::from_string(line);
+    auto rs = dice::roller()(d);
+    window << "Result of '" << d << "': ";
+    print(window, rs);
+  });
+  return 0;
+}

+ 172 - 4
dice-roll.xcodeproj/project.pbxproj

@@ -26,6 +26,12 @@
 		CDEE790225B336EC00F195F9 /* io.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CDEE790125B336EC00F195F9 /* io.cxx */; };
 		CDEE7A6B25B34DAA00F195F9 /* die_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CDEE7A6A25B34DAA00F195F9 /* die_test.cxx */; };
 		CDEE7A7525B35EEC00F195F9 /* io_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CDEE7A7425B35EEC00F195F9 /* io_test.cxx */; };
+		CDEECC322C50497B000C4392 /* terminal_helper.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD38F54E21C945C2007A732C /* terminal_helper.cxx */; };
+		CDEECC342C50497B000C4392 /* libdice.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CD38F50921C83912007A732C /* libdice.a */; };
+		CDEECC452C5049DD000C4392 /* main.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CDEECC442C5049DD000C4392 /* main.cxx */; };
+		CDEECC4A2C50515B000C4392 /* libncurses.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = CDEECC482C50515B000C4392 /* libncurses.tbd */; };
+		CDEECC4B2C50515B000C4392 /* libcurses.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = CDEECC492C50515B000C4392 /* libcurses.tbd */; };
+		CDEECC5A2C50837C000C4392 /* libncurses-wrapper.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CDEECC572C508368000C4392 /* libncurses-wrapper.a */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -141,6 +147,27 @@
 			remoteGlobalIDString = 05818F901A685AEA0072A469;
 			remoteInfo = GoogleMockTests;
 		};
+		CDEECC2F2C50497B000C4392 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CDED6A1B21B2F28A00AB91D0 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = CD38F50821C83912007A732C;
+			remoteInfo = dice;
+		};
+		CDEECC562C508368000C4392 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CDEECC522C508368000C4392 /* ncurses-wrapper.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = CD93A7CD2C5015C000754263;
+			remoteInfo = "ncurses-wrapper";
+		};
+		CDEECC582C50836E000C4392 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CDEECC522C508368000C4392 /* ncurses-wrapper.xcodeproj */;
+			proxyType = 1;
+			remoteGlobalIDString = CD93A7CC2C5015C000754263;
+			remoteInfo = "ncurses-wrapper";
+		};
 /* End PBXContainerItemProxy section */
 
 /* Begin PBXCopyFilesBuildPhase section */
@@ -162,6 +189,15 @@
 			);
 			runOnlyForDeploymentPostprocessing = 1;
 		};
+		CDEECC352C50497B000C4392 /* CopyFiles */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = /usr/share/man/man1/;
+			dstSubfolderSpec = 0;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 1;
+		};
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
@@ -191,6 +227,12 @@
 		CDEE7A5925B3437D00F195F9 /* terminal_helper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = terminal_helper.h; sourceTree = "<group>"; };
 		CDEE7A6A25B34DAA00F195F9 /* die_test.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = die_test.cxx; sourceTree = "<group>"; };
 		CDEE7A7425B35EEC00F195F9 /* io_test.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = io_test.cxx; sourceTree = "<group>"; };
+		CDEECC392C50497B000C4392 /* curses_dice */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = curses_dice; sourceTree = BUILT_PRODUCTS_DIR; };
+		CDEECC412C504998000C4392 /* libncurses-wrapper.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libncurses-wrapper.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+		CDEECC442C5049DD000C4392 /* main.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main.cxx; sourceTree = "<group>"; };
+		CDEECC482C50515B000C4392 /* libncurses.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libncurses.tbd; path = usr/lib/libncurses.tbd; sourceTree = SDKROOT; };
+		CDEECC492C50515B000C4392 /* libcurses.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libcurses.tbd; path = usr/lib/libcurses.tbd; sourceTree = SDKROOT; };
+		CDEECC522C508368000C4392 /* ncurses-wrapper.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = "ncurses-wrapper.xcodeproj"; path = "external/ncurses-wrapper/ncurses-wrapper.xcodeproj"; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -227,6 +269,17 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		CDEECC332C50497B000C4392 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				CDEECC4A2C50515B000C4392 /* libncurses.tbd in Frameworks */,
+				CDEECC4B2C50515B000C4392 /* libcurses.tbd in Frameworks */,
+				CDEECC342C50497B000C4392 /* libdice.a in Frameworks */,
+				CDEECC5A2C50837C000C4392 /* libncurses-wrapper.a in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
@@ -296,6 +349,7 @@
 			isa = PBXGroup;
 			children = (
 				CDC7488C25312DBF008D9D1D /* GoogleMock.xcodeproj */,
+				CDEECC522C508368000C4392 /* ncurses-wrapper.xcodeproj */,
 				CD5B3F5829D8EBD50028CD41 /* expect.xcodeproj */,
 				CD5B3F4E29D8EBD10028CD41 /* scope_guard.xcodeproj */,
 				CD5B3F6229D8EBD90028CD41 /* shared_random_generator.xcodeproj */,
@@ -303,6 +357,7 @@
 				CDED6A2D21B2F2B200AB91D0 /* test */,
 				CDED6A2521B2F28A00AB91D0 /* src */,
 				CD38F52721C87771007A732C /* dice-td */,
+				CDEECC432C5049CC000C4392 /* curses_dice */,
 				CD38F55E21C9485F007A732C /* simple_dice */,
 				CD38F55521C9482A007A732C /* stateful_dice */,
 				CDED6A2421B2F28A00AB91D0 /* Products */,
@@ -317,6 +372,7 @@
 				CD38F50921C83912007A732C /* libdice.a */,
 				CD38F52621C87771007A732C /* dice-td.xctest */,
 				CD38F55421C9482A007A732C /* stateful_dice */,
+				CDEECC392C50497B000C4392 /* curses_dice */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -351,11 +407,30 @@
 		CDED6A5F21B2F89900AB91D0 /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
+				CDEECC492C50515B000C4392 /* libcurses.tbd */,
+				CDEECC482C50515B000C4392 /* libncurses.tbd */,
+				CDEECC412C504998000C4392 /* libncurses-wrapper.a */,
 				CDED6A6021B2F89900AB91D0 /* libshared_random_generator.dylib */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";
 		};
+		CDEECC432C5049CC000C4392 /* curses_dice */ = {
+			isa = PBXGroup;
+			children = (
+				CDEECC442C5049DD000C4392 /* main.cxx */,
+			);
+			path = curses_dice;
+			sourceTree = "<group>";
+		};
+		CDEECC532C508368000C4392 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				CDEECC572C508368000C4392 /* libncurses-wrapper.a */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
 /* End PBXGroup section */
 
 /* Begin PBXHeadersBuildPhase section */
@@ -443,6 +518,25 @@
 			productReference = CDED6A2321B2F28A00AB91D0 /* simple_dice */;
 			productType = "com.apple.product-type.tool";
 		};
+		CDEECC2D2C50497B000C4392 /* curses_dice */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = CDEECC362C50497B000C4392 /* Build configuration list for PBXNativeTarget "curses_dice" */;
+			buildPhases = (
+				CDEECC302C50497B000C4392 /* Sources */,
+				CDEECC332C50497B000C4392 /* Frameworks */,
+				CDEECC352C50497B000C4392 /* CopyFiles */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				CDEECC592C50836E000C4392 /* PBXTargetDependency */,
+				CDEECC2E2C50497B000C4392 /* PBXTargetDependency */,
+			);
+			name = curses_dice;
+			productName = "dice-roll";
+			productReference = CDEECC392C50497B000C4392 /* curses_dice */;
+			productType = "com.apple.product-type.tool";
+		};
 /* End PBXNativeTarget section */
 
 /* Begin PBXProject section */
@@ -486,6 +580,10 @@
 					ProductGroup = CDC7488D25312DBF008D9D1D /* Products */;
 					ProjectRef = CDC7488C25312DBF008D9D1D /* GoogleMock.xcodeproj */;
 				},
+				{
+					ProductGroup = CDEECC532C508368000C4392 /* Products */;
+					ProjectRef = CDEECC522C508368000C4392 /* ncurses-wrapper.xcodeproj */;
+				},
 				{
 					ProductGroup = CD5B3F4F29D8EBD10028CD41 /* Products */;
 					ProjectRef = CD5B3F4E29D8EBD10028CD41 /* scope_guard.xcodeproj */;
@@ -501,6 +599,7 @@
 				CD38F50821C83912007A732C /* dice */,
 				CD38F52521C87771007A732C /* dice-td */,
 				CD38F55321C9482A007A732C /* stateful_dice */,
+				CDEECC2D2C50497B000C4392 /* curses_dice */,
 			);
 		};
 /* End PBXProject section */
@@ -576,6 +675,13 @@
 			remoteRef = CDC7489925312DBF008D9D1D /* PBXContainerItemProxy */;
 			sourceTree = BUILT_PRODUCTS_DIR;
 		};
+		CDEECC572C508368000C4392 /* libncurses-wrapper.a */ = {
+			isa = PBXReferenceProxy;
+			fileType = archive.ar;
+			path = "libncurses-wrapper.a";
+			remoteRef = CDEECC562C508368000C4392 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
 /* End PBXReferenceProxy section */
 
 /* Begin PBXResourcesBuildPhase section */
@@ -631,6 +737,15 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		CDEECC302C50497B000C4392 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				CDEECC322C50497B000C4392 /* terminal_helper.cxx in Sources */,
+				CDEECC452C5049DD000C4392 /* main.cxx in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXSourcesBuildPhase section */
 
 /* Begin PBXTargetDependency section */
@@ -664,6 +779,16 @@
 			name = shared_random_generator;
 			targetProxy = CD5B3F7229D8EBF00028CD41 /* PBXContainerItemProxy */;
 		};
+		CDEECC2E2C50497B000C4392 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = CD38F50821C83912007A732C /* dice */;
+			targetProxy = CDEECC2F2C50497B000C4392 /* PBXContainerItemProxy */;
+		};
+		CDEECC592C50836E000C4392 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			name = "ncurses-wrapper";
+			targetProxy = CDEECC582C50836E000C4392 /* PBXContainerItemProxy */;
+		};
 /* End PBXTargetDependency section */
 
 /* Begin XCBuildConfiguration section */
@@ -755,7 +880,7 @@
 				ALWAYS_SEARCH_USER_PATHS = NO;
 				CLANG_ANALYZER_NONNULL = YES;
 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "c++17";
+				CLANG_CXX_LANGUAGE_STANDARD = "c++20";
 				CLANG_CXX_LIBRARY = "libc++";
 				CLANG_ENABLE_MODULES = YES;
 				CLANG_ENABLE_OBJC_ARC = YES;
@@ -805,7 +930,7 @@
 					"$(BUILT_PRODUCTS_DIR)/usr/local/include",
 					/opt/local/include,
 				);
-				MACOSX_DEPLOYMENT_TARGET = 10.15;
+				MACOSX_DEPLOYMENT_TARGET = 12.3;
 				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
 				MTL_FAST_MATH = YES;
 				ONLY_ACTIVE_ARCH = YES;
@@ -820,7 +945,7 @@
 				ALWAYS_SEARCH_USER_PATHS = NO;
 				CLANG_ANALYZER_NONNULL = YES;
 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "c++17";
+				CLANG_CXX_LANGUAGE_STANDARD = "c++20";
 				CLANG_CXX_LIBRARY = "libc++";
 				CLANG_ENABLE_MODULES = YES;
 				CLANG_ENABLE_OBJC_ARC = YES;
@@ -864,7 +989,7 @@
 					"$(BUILT_PRODUCTS_DIR)/usr/local/include",
 					/opt/local/include,
 				);
-				MACOSX_DEPLOYMENT_TARGET = 10.15;
+				MACOSX_DEPLOYMENT_TARGET = 12.3;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				MTL_FAST_MATH = YES;
 				SDKROOT = macosx;
@@ -898,6 +1023,40 @@
 			};
 			name = Release;
 		};
+		CDEECC372C50497B000C4392 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CODE_SIGN_IDENTITY = "-";
+				CODE_SIGN_STYLE = Automatic;
+				HEADER_SEARCH_PATHS = (
+					"$(BUILT_PRODUCTS_DIR)/usr/local/include",
+					/opt/local/include,
+				);
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				USER_HEADER_SEARCH_PATHS = (
+					"$(PROJECT_DIR)/include/",
+					"$(PROJECT_DIR)/src/",
+				);
+			};
+			name = Debug;
+		};
+		CDEECC382C50497B000C4392 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CODE_SIGN_IDENTITY = "-";
+				CODE_SIGN_STYLE = Automatic;
+				HEADER_SEARCH_PATHS = (
+					"$(BUILT_PRODUCTS_DIR)/usr/local/include",
+					/opt/local/include,
+				);
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				USER_HEADER_SEARCH_PATHS = (
+					"$(PROJECT_DIR)/include/",
+					"$(PROJECT_DIR)/src/",
+				);
+			};
+			name = Release;
+		};
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
@@ -946,6 +1105,15 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
+		CDEECC362C50497B000C4392 /* Build configuration list for PBXNativeTarget "curses_dice" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				CDEECC372C50497B000C4392 /* Debug */,
+				CDEECC382C50497B000C4392 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 /* End XCConfigurationList section */
 	};
 	rootObject = CDED6A1B21B2F28A00AB91D0 /* Project object */;

+ 78 - 0
dice-roll.xcodeproj/xcshareddata/xcschemes/curses_dice.xcscheme

@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1340"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "CDEECC2D2C50497B000C4392"
+               BuildableName = "curses_dice"
+               BlueprintName = "curses_dice"
+               ReferencedContainer = "container:dice-roll.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+      </Testables>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "CDEECC2D2C50497B000C4392"
+            BuildableName = "curses_dice"
+            BlueprintName = "curses_dice"
+            ReferencedContainer = "container:dice-roll.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "CDEECC2D2C50497B000C4392"
+            BuildableName = "curses_dice"
+            BlueprintName = "curses_dice"
+            ReferencedContainer = "container:dice-roll.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 1 - 0
external/ncurses-wrapper

@@ -0,0 +1 @@
+Subproject commit cbbdb7346e23c442bdca80110fde46815cfbd05a

+ 1 - 0
include/dice-roll/roll.h

@@ -23,6 +23,7 @@ struct die_roll {
   // Since this roll was composed on NdM, rolled.size() == N. Each element
   // of rolled is within the integer range [1, M].
   std::vector<int> rolled;
+  int sides;
 };
 
 // Describe the actual result of rolling an arbitrary set of dice with mods

+ 1 - 1
src/roll.cxx

@@ -46,7 +46,7 @@ die_roll roll_impl(die const & d, engine::random & gen) {
   default:
     break;
   }
-  return {d.sgn, hits};
+  return {d.sgn, hits, d.sides};
 }
 
 dice_roll roll_impl(dice const & d, engine::random & gen) {