diff --git a/83 Stock Market/csharp/Game.csproj b/83 Stock Market/csharp/Game.csproj
new file mode 100644
index 00000000..20827042
--- /dev/null
+++ b/83 Stock Market/csharp/Game.csproj
@@ -0,0 +1,8 @@
+
+
+
+ Exe
+ net5.0
+
+
+
diff --git a/83 Stock Market/csharp/StockMarket.sln b/83 Stock Market/csharp/StockMarket.sln
new file mode 100644
index 00000000..5bfb67aa
--- /dev/null
+++ b/83 Stock Market/csharp/StockMarket.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.31321.278
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Game", "Game.csproj", "{BADD262D-D540-431F-8803-2A6F80C22033}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {BADD262D-D540-431F-8803-2A6F80C22033}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BADD262D-D540-431F-8803-2A6F80C22033}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BADD262D-D540-431F-8803-2A6F80C22033}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BADD262D-D540-431F-8803-2A6F80C22033}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {C78DBA4A-87E2-4B31-A261-4AEF5E4C3B12}
+ EndGlobalSection
+EndGlobal
diff --git a/83 Stock Market/csharp/src/Assets.cs b/83 Stock Market/csharp/src/Assets.cs
new file mode 100644
index 00000000..ecb2eafe
--- /dev/null
+++ b/83 Stock Market/csharp/src/Assets.cs
@@ -0,0 +1,20 @@
+using System.Collections.Immutable;
+
+namespace Game
+{
+ ///
+ /// Stores the player's assets.
+ ///
+ public record Assets
+ {
+ ///
+ /// Gets the player's amount of cash.
+ ///
+ public double Cash { get; init; }
+
+ ///
+ /// Gets the number of stocks owned of each company.
+ ///
+ public ImmutableArray Portfolio { get; init; }
+ }
+}
diff --git a/83 Stock Market/csharp/src/Broker.cs b/83 Stock Market/csharp/src/Broker.cs
new file mode 100644
index 00000000..dc0abb9e
--- /dev/null
+++ b/83 Stock Market/csharp/src/Broker.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+
+namespace Game
+{
+ ///
+ /// Contains functions for exchanging assets.
+ ///
+ public static class Broker
+ {
+ ///
+ /// Applies the given set of transactions to the given set of assets.
+ ///
+ ///
+ /// The assets to update.
+ ///
+ ///
+ /// The set of stocks to purchase or sell. Positive values indicate
+ /// purchaes and negative values indicate sales.
+ ///
+ ///
+ /// The collection of companies.
+ ///
+ ///
+ /// Returns the sellers new assets and a code indicating the result
+ /// of the transaction.
+ ///
+ public static (Assets newAssets, TransactionResult result) Apply(Assets assets, IEnumerable transactions, IEnumerable companies)
+ {
+ var (netCost, transactionSize) = Enumerable.Zip(
+ transactions,
+ companies,
+ (amount, company) => (amount * company.SharePrice))
+ .Aggregate(
+ (netCost: 0.0, transactionSize: 0.0),
+ (accumulated, amount) => (accumulated.netCost + amount, accumulated.transactionSize + Math.Abs(amount)));
+
+ var brokerageFee = 0.01 * transactionSize;
+
+ var newAssets = assets with
+ {
+ Cash = assets.Cash - netCost - brokerageFee,
+ Portfolio = ImmutableArray.CreateRange(Enumerable.Zip(
+ assets.Portfolio,
+ transactions,
+ (sharesOwned, delta) => sharesOwned + delta))
+ };
+
+ if (newAssets.Portfolio.Any(amount => amount < 0))
+ return (newAssets, TransactionResult.Oversold);
+ else
+ if (newAssets.Cash < 0)
+ return (newAssets, TransactionResult.Overspent);
+ else
+ return (newAssets, TransactionResult.Ok);
+ }
+ }
+}
diff --git a/83 Stock Market/csharp/src/Company.cs b/83 Stock Market/csharp/src/Company.cs
new file mode 100644
index 00000000..59f40e8d
--- /dev/null
+++ b/83 Stock Market/csharp/src/Company.cs
@@ -0,0 +1,23 @@
+namespace Game
+{
+ ///
+ /// Represents a company.
+ ///
+ public record Company
+ {
+ ///
+ /// Gets the company's name.
+ ///
+ public string Name { get; init; }
+
+ ///
+ /// Gets the company's three letter stock symbol.
+ ///
+ public string StockSymbol { get; init; }
+
+ ///
+ /// Gets the company's current share price.
+ ///
+ public double SharePrice { get; init; }
+ }
+}
diff --git a/83 Stock Market/csharp/src/Controller.cs b/83 Stock Market/csharp/src/Controller.cs
new file mode 100644
index 00000000..bccfde0c
--- /dev/null
+++ b/83 Stock Market/csharp/src/Controller.cs
@@ -0,0 +1,107 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Game
+{
+ public static class Controller
+ {
+ ///
+ /// Manages the initial interaction with the user.
+ ///
+ public static void StartGame()
+ {
+ View.ShowBanner();
+
+ var showInstructions = GetYesOrNo(View.PromptShowInstructions);
+ View.ShowSeparator();
+ if (showInstructions)
+ View.ShowInstructions();
+
+ View.ShowSeparator();
+ }
+
+ ///
+ /// Gets a yes or no answer from the user.
+ ///
+ ///
+ /// Displays the prompt.
+ ///
+ ///
+ /// True if the user answered yes and false if he or she answered no.
+ ///
+ public static bool GetYesOrNo(Action prompt)
+ {
+ prompt();
+
+ var response = default(char);
+ do
+ {
+ response = Console.ReadKey(intercept: true).KeyChar;
+ }
+ while (response != '0' && response != '1');
+
+ View.ShowChar(response);
+ return response == '1';
+ }
+
+ ///
+ /// Gets a transaction amount for each company in the given collection
+ /// of companies and returns the updated assets.
+ ///
+ ///
+ /// The assets to update.
+ ///
+ ///
+ /// The collection of companies.
+ ///
+ ///
+ /// The updated assets.
+ ///
+ public static Assets UpdateAssets(Assets assets, IEnumerable companies)
+ {
+ while (true)
+ {
+ View.PromptEnterTransactions();
+
+ var result = Broker.Apply (
+ assets,
+ companies.Select(GetTransactionAmount).ToList(),
+ companies);
+
+ switch (result)
+ {
+ case (Assets newAssets, TransactionResult.Ok):
+ return newAssets;
+ case (_, TransactionResult.Oversold):
+ View.ShowOversold();
+ break;
+ case (Assets newAssets, TransactionResult.Overspent):
+ View.ShowOverspent(-newAssets.Cash);
+ break;
+ }
+ }
+ }
+
+ ///
+ /// Gets a transaction amount for the given company.
+ ///
+ ///
+ /// The company to buy or sell.
+ ///
+ ///
+ /// The number of shares to buy or sell.
+ ///
+ public static int GetTransactionAmount(Company company)
+ {
+ while (true)
+ {
+ View.PromptBuySellCompany(company);
+ if (Int32.TryParse(Console.ReadLine(), out var amount))
+ return amount;
+ else
+ View.PromptValidInteger();
+ }
+ }
+ }
+}
diff --git a/83 Stock Market/csharp/src/Extensions/EnumerableExtensions.cs b/83 Stock Market/csharp/src/Extensions/EnumerableExtensions.cs
new file mode 100644
index 00000000..11de50a9
--- /dev/null
+++ b/83 Stock Market/csharp/src/Extensions/EnumerableExtensions.cs
@@ -0,0 +1,116 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Game.Extensions
+{
+ ///
+ /// Provides additional methods for the
+ /// interface.
+ ///
+ public static class EnumerableExtensions
+ {
+ ///
+ /// Simultaneously projects each element of a sequence and applies
+ /// the result of the previous projection.
+ ///
+ ///
+ /// The type of elements in the source sequence.
+ ///
+ ///
+ /// The type of elements in the result sequence.
+ ///
+ ///
+ /// The source sequence.
+ ///
+ ///
+ /// The seed value for the aggregation component. This value is
+ /// passed to the first call to .
+ ///
+ ///
+ /// The projection function. This function is supplied with a value
+ /// from the source sequence and the result of the projection on the
+ /// previous value in the source sequence.
+ ///
+ ///
+ /// The resulting sequence.
+ ///
+ public static IEnumerable SelectAndAggregate(
+ this IEnumerable source,
+ TResult seed,
+ Func selector)
+ {
+ foreach (var element in source)
+ {
+ seed = selector(element, seed);
+ yield return seed;
+ }
+ }
+
+ ///
+ /// Combines the results of three distinct sequences into a single
+ /// sequence.
+ ///
+ ///
+ /// The element type of the first sequence.
+ ///
+ ///
+ /// The element type of the second sequence.
+ ///
+ ///
+ /// The element type of the third sequence.
+ ///
+ ///
+ /// The element type of the resulting sequence.
+ ///
+ ///
+ /// The first source sequence.
+ ///
+ ///
+ /// The second source sequence.
+ ///
+ ///
+ /// The third source sequence.
+ ///
+ ///
+ /// Function that combines results from each source sequence into a
+ /// final result.
+ ///
+ ///
+ /// A sequence of combined values.
+ ///
+ ///
+ ///
+ /// This function works identically to Enumerable.Zip except that it
+ /// combines three sequences instead of two.
+ ///
+ ///
+ /// We have defined this as an extension method for consistency with
+ /// the similar LINQ methods in the class.
+ /// However, since there is nothing special about the first sequence,
+ /// it is often more clear to call this as a regular function. For
+ /// example:
+ ///
+ ///
+ /// EnumerableExtensions.Zip(
+ /// sequence1,
+ /// sequence2,
+ /// sequence3,
+ /// (a, b, c) => GetResult (a, b, c));
+ ///
+ ///
+ public static IEnumerable Zip(
+ this IEnumerable first,
+ IEnumerable second,
+ IEnumerable third,
+ Func resultSelector)
+ {
+ using var enumerator1 = first.GetEnumerator();
+ using var enumerator2 = second.GetEnumerator();
+ using var enumerator3 = third.GetEnumerator();
+
+ while (enumerator1.MoveNext() && enumerator2.MoveNext() && enumerator3.MoveNext())
+ yield return resultSelector(enumerator1.Current, enumerator2.Current, enumerator3.Current);
+ }
+ }
+}
diff --git a/83 Stock Market/csharp/src/Extensions/RandomExtensions.cs b/83 Stock Market/csharp/src/Extensions/RandomExtensions.cs
new file mode 100644
index 00000000..0c3a4230
--- /dev/null
+++ b/83 Stock Market/csharp/src/Extensions/RandomExtensions.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+
+namespace Game.Extensions
+{
+ ///
+ /// Provides additional methods for the class.
+ ///
+ public static class RandomExtensions
+ {
+ ///
+ /// Generates an infinite sequence of random numbers.
+ ///
+ ///
+ /// The random number generator.
+ ///
+ ///
+ /// The inclusive lower bound of the range to generate.
+ ///
+ ///
+ /// The exclusive upper bound of the range to generate.
+ ///
+ ///
+ /// An infinite sequence of random integers in the range [min, max).
+ ///
+ ///
+ ///
+ /// We use an exclusive upper bound, even though it's a little
+ /// confusing, for the sake of consistency with Random.Next.
+ ///
+ ///
+ /// Since the sequence is infinite, a typical usage would be to cap
+ /// the results with a function like Enumerable.Take. For example,
+ /// to sum the results of rolling three six sided dice, we could do:
+ ///
+ ///
+ /// random.Integers(1, 7).Take(3).Sum()
+ ///
+ ///
+ public static IEnumerable Integers(this Random random, int min, int max)
+ {
+ while (true)
+ yield return random.Next(min, max);
+ }
+ }
+}
diff --git a/83 Stock Market/csharp/src/Program.cs b/83 Stock Market/csharp/src/Program.cs
new file mode 100644
index 00000000..552d8c9a
--- /dev/null
+++ b/83 Stock Market/csharp/src/Program.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Immutable;
+using System.Linq;
+
+namespace Game
+{
+ class Program
+ {
+ ///
+ /// Defines the set of companies that will be simulated in the game.
+ ///
+ private static ImmutableArray Companies = ImmutableArray.CreateRange(new[]
+ {
+ new Company { Name = "INT. BALLISTIC MISSILES", StockSymbol = "IBM", SharePrice = 100 },
+ new Company { Name = "RED CROSS OF AMERICA", StockSymbol = "RCA", SharePrice = 85 },
+ new Company { Name = "LICHTENSTEIN, BUMRAP & JOKE", StockSymbol = "LBJ", SharePrice = 150 },
+ new Company { Name = "AMERICAN BANKRUPT CO.", StockSymbol = "ABC", SharePrice = 140 },
+ new Company { Name = "CENSURED BOOKS STORE", StockSymbol = "CBS", SharePrice = 110 }
+ });
+
+ static void Main()
+ {
+ var assets = new Assets
+ {
+ Cash = 10000.0,
+ Portfolio = ImmutableArray.CreateRange(Enumerable.Repeat(0, Companies.Length))
+ };
+
+ var previousDay = default(TradingDay);
+
+ Controller.StartGame();
+
+ foreach (var day in StockMarket.Simulate(Companies))
+ {
+ if (previousDay is null)
+ View.ShowCompanies(day.Companies);
+ else
+ View.ShowTradeResults(day, previousDay, assets);
+
+ View.ShowAssets(assets, day.Companies);
+
+ if (previousDay is not null && !Controller.GetYesOrNo(View.PromptContinue))
+ break;
+
+ assets = Controller.UpdateAssets(assets, day.Companies);
+ previousDay = day;
+ }
+
+ View.ShowFarewell();
+ }
+ }
+}
diff --git a/83 Stock Market/csharp/src/StockMarket.cs b/83 Stock Market/csharp/src/StockMarket.cs
new file mode 100644
index 00000000..ba2f52ee
--- /dev/null
+++ b/83 Stock Market/csharp/src/StockMarket.cs
@@ -0,0 +1,149 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using Game.Extensions;
+
+namespace Game
+{
+ ///
+ /// Provides a method for simulating a stock market.
+ ///
+ public static class StockMarket
+ {
+ ///
+ /// Simulates changes in the stock market over time.
+ ///
+ ///
+ /// The collection of companies that will participate in the market.
+ ///
+ ///
+ /// An infinite sequence of trading days. Each day represents the
+ /// state of the stock market at the start of that day.
+ ///
+ public static IEnumerable Simulate(ImmutableArray companies)
+ {
+ var random = new Random();
+
+ var cyclicParameters = EnumerableExtensions.Zip(
+ Trends(random, 1, 5),
+ PriceSpikes(random, companies.Length, 1, 5),
+ PriceSpikes(random, companies.Length, 1, 5),
+ (trend, company1, company2) => (trend, positiveSpike: company1, negativeSpike: company2));
+
+ return cyclicParameters.SelectAndAggregate(
+ new TradingDay
+ {
+ Companies = companies
+ },
+ (parameters, previousDay) => previousDay with
+ {
+ Companies = ImmutableArray.CreateRange(
+ previousDay.Companies.Select ((company, index) => AdjustSharePrice(
+ random,
+ company,
+ parameters.trend,
+ parameters.positiveSpike == index,
+ parameters.negativeSpike == index)))
+ });
+ }
+
+ ///
+ /// Creates a copy of a company with a randomly adjusted share price,
+ /// based on the given parameters.
+ ///
+ ///
+ /// The random number generator.
+ ///
+ ///
+ /// The company to adjust.
+ ///
+ ///
+ /// The slope of the overall market price trend.
+ ///
+ ///
+ /// True if the function should simulate a positive spike in the
+ /// company's share price.
+ ///
+ ///
+ /// True if the function should simulate a negative spike in the
+ /// company's share price.
+ ///
+ ///
+ /// The adjusted company.
+ ///
+ private static Company AdjustSharePrice(Random random, Company company, double trend, bool positiveSpike, bool negativeSpike)
+ {
+ var boost = random.Next(4) * 0.25;
+
+ var spikeAmount = 0.0;
+
+ if (positiveSpike)
+ spikeAmount = 10;
+
+ if (negativeSpike)
+ spikeAmount = spikeAmount - 10;
+
+ var priceChange = (int)(trend * company.SharePrice) + boost + (int)(3.5 - (6 * random.NextDouble())) + spikeAmount;
+
+ var newPrice = company.SharePrice + priceChange;
+ if (newPrice < 0)
+ newPrice = 0;
+
+ return company with { SharePrice = newPrice };
+ }
+
+ ///
+ /// Generates an infinite sequence of market trends.
+ ///
+ ///
+ /// The random number generator.
+ ///
+ ///
+ /// The minimum number of days each trend should last.
+ ///
+ ///
+ /// The maximum number of days each trend should last.
+ ///
+ public static IEnumerable Trends(Random random, int minDays, int maxDays) =>
+ random.Integers(minDays, maxDays + 1).SelectMany(days => Enumerable.Repeat(GenerateTrend(random), days));
+
+ ///
+ /// Generates a random value for the market trend.
+ ///
+ ///
+ /// The random number generator.
+ ///
+ ///
+ /// A trend value in the range [-0.1, 0.1].
+ ///
+ private static double GenerateTrend(Random random) =>
+ ((int)(random.NextDouble() * 10 + 0.5) / 100.0) * (random.Next(2) == 0 ? 1 : -1) ;
+
+ ///
+ /// Generates an infinite sequence of price spikes.
+ ///
+ ///
+ /// The random number generator.
+ ///
+ ///
+ /// The number of companies.
+ ///
+ ///
+ /// The minimum number of days in between price spikes.
+ ///
+ ///
+ /// The maximum number of days in between price spikes.
+ ///
+ ///
+ /// An infinite sequence of random company indexes and null values.
+ /// A non-null value means that the corresponding company should
+ /// experience a price spike.
+ ///
+ private static IEnumerable PriceSpikes(Random random, int companyCount, int minDays, int maxDays) =>
+ random.Integers(minDays, maxDays + 1)
+ .SelectMany(
+ days => Enumerable.Range(0, days),
+ (days, dayNumber) => dayNumber == 0 ? random.Next(companyCount) : default(int?));
+ }
+}
diff --git a/83 Stock Market/csharp/src/TradingDay.cs b/83 Stock Market/csharp/src/TradingDay.cs
new file mode 100644
index 00000000..4e9d93f4
--- /dev/null
+++ b/83 Stock Market/csharp/src/TradingDay.cs
@@ -0,0 +1,24 @@
+using System.Collections.Immutable;
+using System.Linq;
+
+namespace Game
+{
+ ///
+ /// Represents a single trading day.
+ ///
+ public record TradingDay
+ {
+ ///
+ /// Gets the average share price of all companies in the market this
+ /// day.
+ ///
+ public double AverageSharePrice =>
+ Companies.Average (company => company.SharePrice);
+
+ ///
+ /// Gets the collection of public listed companies in the stock market
+ /// this day.
+ ///
+ public ImmutableArray Companies { get; init; }
+ }
+}
diff --git a/83 Stock Market/csharp/src/TransactionResult.cs b/83 Stock Market/csharp/src/TransactionResult.cs
new file mode 100644
index 00000000..23ee8831
--- /dev/null
+++ b/83 Stock Market/csharp/src/TransactionResult.cs
@@ -0,0 +1,25 @@
+namespace Game
+{
+ ///
+ /// Enumerates the different possible outcomes of applying a transaction.
+ ///
+ public enum TransactionResult
+ {
+ ///
+ /// The transaction was successful.
+ ///
+ Ok,
+
+ ///
+ /// The transaction failed because the seller tried to sell more shares
+ /// than he or she owns.
+ ///
+ Oversold,
+
+ ///
+ /// The transaction failed because the net cost was greater than the
+ /// seller's available cash.
+ ///
+ Overspent
+ }
+}
diff --git a/83 Stock Market/csharp/src/View.cs b/83 Stock Market/csharp/src/View.cs
new file mode 100644
index 00000000..28426b98
--- /dev/null
+++ b/83 Stock Market/csharp/src/View.cs
@@ -0,0 +1,157 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Game.Extensions;
+
+namespace Game
+{
+ ///
+ /// Contains functions for displaying information to the user.
+ ///
+ public static class View
+ {
+ public static void ShowBanner()
+ {
+ Console.WriteLine(" STOCK MARKET");
+ Console.WriteLine(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
+ Console.WriteLine();
+ Console.WriteLine();
+ Console.WriteLine();
+ }
+
+ public static void ShowInstructions()
+ {
+ Console.WriteLine("THIS PROGRAM PLAYS THE STOCK MARKET. YOU WILL BE GIVEN");
+ Console.WriteLine("$10,000 AND MAY BUY OR SELL STOCKS. THE STOCK PRICES WILL");
+ Console.WriteLine("BE GENERATED RANDOMLY AND THEREFORE THIS MODEL DOES NOT");
+ Console.WriteLine("REPRESENT EXACTLY WHAT HAPPENS ON THE EXCHANGE. A TABLE");
+ Console.WriteLine("OF AVAILABLE STOCKS, THEIR PRICES, AND THE NUMBER OF SHARES");
+ Console.WriteLine("IN YOUR PORTFOLIO WILL BE PRINTED. FOLLOWING THIS, THE");
+ Console.WriteLine("INITIALS OF EACH STOCK WILL BE PRINTED WITH A QUESTION");
+ Console.WriteLine("MARK. HERE YOU INDICATE A TRANSACTION. TO BUY A STOCK");
+ Console.WriteLine("TYPE +NNN, TO SELL A STOCK TYPE -NNN, WHERE NNN IS THE");
+ Console.WriteLine("NUMBER OF SHARES. A BROKERAGE FEE OF 1% WILL BE CHARGED");
+ Console.WriteLine("ON ALL TRANSACTIONS. NOTE THAT IF A STOCK'S VALUE DROPS");
+ Console.WriteLine("TO ZERO IT MAY REBOUND TO A POSITIVE VALUE AGAIN. YOU");
+ Console.WriteLine("HAVE $10,000 TO INVEST. USE INTEGERS FOR ALL YOUR INPUTS.");
+ Console.WriteLine("(NOTE: TO GET A 'FEEL' FOR THE MARKET RUN FOR AT LEAST");
+ Console.WriteLine("10 DAYS)");
+ Console.WriteLine("-----GOOD LUCK!-----");
+ }
+
+ public static void ShowCompanies(IEnumerable companies)
+ {
+ var maxNameLength = companies.Max(company => company.Name.Length);
+
+ Console.WriteLine($"{"STOCK".PadRight(maxNameLength)} INITIALS PRICE/SHARE");
+ foreach (var company in companies)
+ Console.WriteLine($"{company.Name.PadRight(maxNameLength)} {company.StockSymbol} {company.SharePrice:0.00}");
+
+ Console.WriteLine();
+ Console.WriteLine($"NEW YORK STOCK EXCHANGE AVERAGE: {companies.Average(company => company.SharePrice):0.00}");
+ Console.WriteLine();
+ }
+
+ public static void ShowTradeResults(TradingDay day, TradingDay previousDay, Assets assets)
+ {
+ var results = EnumerableExtensions.Zip(
+ day.Companies,
+ previousDay.Companies,
+ assets.Portfolio,
+ (company, previous, shares) =>
+ (
+ stockSymbol: company.StockSymbol,
+ price: company.SharePrice,
+ shares,
+ value: shares * company.SharePrice,
+ change: company.SharePrice - previous.SharePrice
+ )).ToList();
+
+ Console.WriteLine();
+ Console.WriteLine();
+ Console.WriteLine("********** END OF DAY'S TRADING **********");
+ Console.WriteLine();
+ Console.WriteLine();
+
+ Console.WriteLine("STOCK\tPRICE/SHARE\tHOLDINGS\tVALUE\tNET PRICE CHANGE");
+ foreach (var result in results)
+ Console.WriteLine($"{result.stockSymbol}\t{result.price}\t\t{result.shares}\t\t{result.value:0.00}\t\t{result.change:0.00}");
+
+ Console.WriteLine();
+ Console.WriteLine();
+ Console.WriteLine();
+
+ var averagePrice = day.AverageSharePrice;
+ var averagePriceChange = averagePrice - previousDay.AverageSharePrice;
+
+ Console.WriteLine($"NEW YORK STOCK EXCHANGE AVERAGE: {averagePrice:0.00} NET CHANGE {averagePriceChange:0.00}");
+ Console.WriteLine();
+ }
+
+ public static void ShowAssets(Assets assets, IEnumerable companies)
+ {
+ var totalStockValue = Enumerable.Zip(
+ assets.Portfolio,
+ companies,
+ (shares, company) => shares * company.SharePrice).Sum();
+
+ Console.WriteLine($"TOTAL STOCK ASSETS ARE ${totalStockValue:0.00}");
+ Console.WriteLine($"TOTAL CASH ASSETS ARE ${assets.Cash:0.00}");
+ Console.WriteLine($"TOTAL ASSETS ARE ${totalStockValue + assets.Cash:0.00}");
+ Console.WriteLine();
+ }
+
+ public static void ShowOversold()
+ {
+ Console.WriteLine();
+ Console.WriteLine("YOU HAVE OVERSOLD A STOCK; TRY AGAIN.");
+ }
+
+ public static void ShowOverspent(double amount)
+ {
+ Console.WriteLine();
+ Console.WriteLine($"YOU HAVE USED ${amount:0.00} MORE THAN YOU HAVE.");
+ }
+
+ public static void ShowFarewell()
+ {
+ Console.WriteLine("HOPE YOU HAD FUN!!");
+ }
+
+ public static void ShowSeparator()
+ {
+ Console.WriteLine();
+ Console.WriteLine();
+ }
+
+ public static void ShowChar(char c)
+ {
+ Console.WriteLine(c);
+ }
+
+ public static void PromptShowInstructions()
+ {
+ Console.Write("DO YOU WANT THE INSTRUCTIONS (YES-TYPE 1, NO-TYPE 0)? ");
+ }
+
+ public static void PromptContinue()
+ {
+ Console.Write("DO YOU WISH TO CONTINUE (YES-TYPE 1, NO-TYPE 0)? ");
+ }
+
+ public static void PromptEnterTransactions()
+ {
+ Console.WriteLine("WHAT IS YOUR TRANSACTION IN");
+ }
+
+ public static void PromptBuySellCompany(Company company)
+ {
+ Console.Write($"{company.StockSymbol}? ");
+ }
+
+ public static void PromptValidInteger()
+ {
+ Console.WriteLine("PLEASE ENTER A VALID INTEGER");
+ }
+ }
+}