This commit is contained in:
masykur
2022-02-26 08:48:11 +07:00
715 changed files with 30613 additions and 1624 deletions
+20
View File
@@ -1,3 +1,17 @@
.local/
.vscode/
.gradle/
node_modules/
buildJvm/bin
buildJvm/*/build/
.classpath
.project
.settings
.metadata
*.iml
*.ipr
*.class
*/.vs
*.suo
@@ -13,6 +27,12 @@ obj/
out/
*.py[co]
Pipfile
.DS_Store
.vs/
**/target/
Cargo.lock
**/*.rs.bk
/target
+168
View File
@@ -0,0 +1,168 @@
root = true
[*.{cs,vb}]
indent_size = 4
indent_style = space
end_of_line = crlf
insert_final_newline = true
dotnet_separate_import_directive_groups = false
dotnet_sort_system_directives_first = false
dotnet_style_qualification_for_event = false:suggestion
dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion
dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:silent
dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:silent
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:silent
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_object_initializer = true:suggestion
dotnet_style_operator_placement_when_wrapping = end_of_line
dotnet_style_prefer_auto_properties = true:suggestion
dotnet_style_prefer_compound_assignment = true:suggestion
dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
dotnet_style_prefer_conditional_expression_over_return = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
dotnet_style_prefer_simplified_interpolation = true:suggestion
# Naming rules
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.non_private_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_private_members_should_be_pascal_case.symbols = non_private_members
dotnet_naming_rule.non_private_members_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.private_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.private_members_should_be_pascal_case.symbols = private_members
dotnet_naming_rule.private_members_should_be_pascal_case.style = camel_case
# Symbols for use with naming rules
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum, delegate
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.non_private_members.applicable_kinds = property, method, field, event
dotnet_naming_symbols.non_private_members.applicable_accessibilities = public, internal, protected, protected_internal, private_protected
dotnet_naming_symbols.private_members.applicable_kinds = property, method, field, event
dotnet_naming_symbols.private_members.applicable_accessibilities = private
# Naming styles
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case
dotnet_naming_style.camel_case.required_prefix =
dotnet_naming_style.camel_case.required_suffix =
dotnet_naming_style.camel_case.word_separator =
dotnet_naming_style.camel_case.capitalization = camel_case
[*.cs]
csharp_new_line_before_catch = false
csharp_new_line_before_else = false
csharp_new_line_before_finally = false
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_open_brace = none
csharp_new_line_between_query_expression_clauses = true
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = true
csharp_indent_labels = one_less_than_current
csharp_indent_switch_labels = true
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = true
csharp_prefer_braces = true:warning
csharp_style_expression_bodied_constructors = true:suggestion
csharp_style_expression_bodied_methods = true:suggestion
csharp_style_expression_bodied_properties = true:suggestion
csharp_prefer_simple_default_expression = true:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
csharp_style_var_elsewhere = true:suggestion
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
csharp_preferred_modifier_order = internal,protected,public,private,static,readonly,abstract,override,sealed,virtual:suggestion
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
csharp_style_deconstructed_variable_declaration = true:suggestion
csharp_style_pattern_local_over_anonymous_function = true:suggestion
csharp_style_throw_expression = true:suggestion
csharp_style_conditional_delegate_call = true:suggestion
[*.vb]
visual_basic_preferred_modifier_order = partial,default,private,protected,public,friend,notoverridable,overridable,mustoverride,overloads,overrides,mustinherit,notinheritable,static,shared,shadows,readonly,writeonly,dim,const,withevents,widening,narrowing,custom,async,iterator:silent
visual_basic_style_unused_value_assignment_preference = unused_local_variable:suggestion
visual_basic_style_unused_value_expression_statement_preference = unused_local_variable:silent
+25
View File
@@ -0,0 +1,25 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.32014.148
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotnetUtils", "DotnetUtils\DotnetUtils.csproj", "{BFDF93C2-4FB7-4838-AFDF-E7B5F83C3F00}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{BFDF93C2-4FB7-4838-AFDF-E7B5F83C3F00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BFDF93C2-4FB7-4838-AFDF-E7B5F83C3F00}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BFDF93C2-4FB7-4838-AFDF-E7B5F83C3F00}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BFDF93C2-4FB7-4838-AFDF-E7B5F83C3F00}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {30FCF56E-4E83-42F8-AB43-A52C86C7C9B4}
EndGlobalSection
EndGlobal
@@ -0,0 +1,30 @@
using System.Diagnostics.CodeAnalysis;
using static System.IO.Path;
namespace DotnetUtils;
public static class Extensions {
public static IEnumerable<TResult> SelectT<T1, T2, TResult>(this IEnumerable<(T1, T2)> src, Func<T1, T2, TResult> selector) =>
src.Select(x => selector(x.Item1, x.Item2));
public static IEnumerable<TResult> SelectT<T1, T2, T3, TResult>(this IEnumerable<(T1, T2, T3)> src, Func<T1, T2, T3, TResult> selector) =>
src.Select(x => selector(x.Item1, x.Item2, x.Item3));
public static IEnumerable<(T1, T2, int)> WithIndex<T1, T2>(this IEnumerable<(T1, T2)> src) => src.Select((x, index) => (x.Item1, x.Item2, index));
public static bool None<T>(this IEnumerable<T> src, Func<T, bool>? predicate = null) =>
predicate is null ?
!src.Any() :
!src.Any(predicate);
public static bool IsNullOrWhitespace([NotNullWhen(false)] this string? s) => string.IsNullOrWhiteSpace(s);
[return: NotNullIfNotNull("path")]
public static string? RelativePath(this string? path, string? rootPath) {
if (
path.IsNullOrWhitespace() ||
rootPath.IsNullOrWhitespace()
) { return path; }
path = path.TrimEnd('\\'); // remove trailing backslash, if present
return GetRelativePath(rootPath, path.TrimEnd('\\'));
}
}
@@ -0,0 +1,35 @@
using System.Xml.Linq;
using static System.Console;
namespace DotnetUtils;
public static class Functions {
public static string? getValue(string path, params string[] names) {
if (names.Length == 0) { throw new InvalidOperationException(); }
var parent = XDocument.Load(path).Element("Project")?.Element("PropertyGroup");
return getValue(parent, names);
}
public static string? getValue(XElement? parent, params string[] names) {
if (names.Length == 0) { throw new InvalidOperationException(); }
XElement? elem = null;
foreach (var name in names) {
elem = parent?.Element(name);
if (elem != null) { break; }
}
return elem?.Value;
}
public static int getChoice(int maxValue) => getChoice(0, maxValue);
public static int getChoice(int minValue, int maxValue) {
int result;
do {
Write("? ");
} while (!int.TryParse(ReadLine(), out result) || result < minValue || result > maxValue);
//WriteLine();
return result;
}
}
@@ -0,0 +1,8 @@
namespace DotnetUtils;
public static class Globals {
public static readonly Dictionary<string, (string codefileExtension, string projExtension)> LangData = new() {
{ "csharp", ("cs", "csproj") },
{ "vbnet", ("vb", "vbproj") }
};
}
@@ -0,0 +1,88 @@
using System.Diagnostics;
namespace DotnetUtils;
public static class Methods {
public static ProcessResult RunProcess(string filename, string arguments) {
var process = new Process() {
StartInfo = {
FileName = filename,
Arguments = arguments,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
},
EnableRaisingEvents = true
};
return RunProcess(process);
}
public static ProcessResult RunProcess(Process process, string input = "") {
var (output, error) = ("", "");
var (redirectOut, redirectErr) = (
process.StartInfo.RedirectStandardOutput,
process.StartInfo.RedirectStandardError
);
if (redirectOut) {
process.OutputDataReceived += (s, ea) => output += ea.Data + "\n";
}
if (redirectErr) {
process.ErrorDataReceived += (s, ea) => error += ea.Data + "\n";
}
if (!process.Start()) {
throw new InvalidOperationException();
};
if (redirectOut) { process.BeginOutputReadLine(); }
if (redirectErr) { process.BeginErrorReadLine(); }
if (!string.IsNullOrEmpty(input)) {
process.StandardInput.WriteLine(input);
process.StandardInput.Close();
}
process.WaitForExit();
return new ProcessResult(process.ExitCode, output, error);
}
public static Task<ProcessResult> RunProcessAsync(Process process, string input = "") {
var tcs = new TaskCompletionSource<ProcessResult>();
var (output, error) = ("", "");
var (redirectOut, redirectErr) = (
process.StartInfo.RedirectStandardOutput,
process.StartInfo.RedirectStandardError
);
process.Exited += (s, e) => tcs.SetResult(new ProcessResult(process.ExitCode, output, error));
if (redirectOut) {
process.OutputDataReceived += (s, ea) => output += ea.Data + "\n";
}
if (redirectErr) {
process.ErrorDataReceived += (s, ea) => error += ea.Data + "\n";
}
if (!process.Start()) {
// what happens to the Exited event if process doesn't start successfully?
throw new InvalidOperationException();
}
if (redirectOut) { process.BeginOutputReadLine(); }
if (redirectErr) { process.BeginErrorReadLine(); }
if (!string.IsNullOrEmpty(input)) {
process.StandardInput.WriteLine(input);
process.StandardInput.Close();
}
return tcs.Task;
}
}
public sealed record ProcessResult(int ExitCode, string StdOut, string StdErr) {
public override string? ToString() =>
StdOut +
(StdOut is not (null or "") && ExitCode > 0 ? "\n" : "") +
(ExitCode != 0 ?
$"{ExitCode}\n{StdErr}" :
"");
}
@@ -0,0 +1,59 @@
using static System.IO.Directory;
using static System.IO.Path;
using static DotnetUtils.Globals;
namespace DotnetUtils;
public record PortInfo(
string GamePath, string FolderName, int Index, string GameName,
string LangPath, string Lang, string Ext, string ProjExt,
string[] CodeFiles, string[] Slns, string[] Projs
) {
private static readonly EnumerationOptions enumerationOptions = new() {
RecurseSubdirectories = true,
MatchType = MatchType.Simple,
MatchCasing = MatchCasing.CaseInsensitive
};
// .NET namespaces cannot have a digit as the first character
// For games whose name starts with a digit, we map the name to a specific string
private static readonly Dictionary<string, string> specialGameNames = new() {
{ "3-D_Plot", "Plot" },
{ "3-D_Tic-Tac-Toe", "ThreeDTicTacToe" },
{ "23_Matches", "TwentyThreeMatches"}
};
public static PortInfo? Create(string gamePath, string langKeyword) {
var folderName = GetFileName(gamePath);
var parts = folderName.Split('_', 2);
if (parts.Length <= 1) { return null; }
var (index, gameName) = (
int.TryParse(parts[0], out var n) && n > 0 ? // ignore utilities folder
n :
(int?)null,
specialGameNames.TryGetValue(parts[1], out var specialName) ?
specialName :
parts[1].Replace("_", "").Replace("-", "")
);
if (index is null || gameName is null) { return null; }
var (ext, projExt) = LangData[langKeyword];
var langPath = Combine(gamePath, langKeyword);
var codeFiles =
GetFiles(langPath, $"*.{ext}", enumerationOptions)
.Where(x => !x.Contains("\\bin\\") && !x.Contains("\\obj\\"))
.ToArray();
return new PortInfo(
gamePath, folderName, index.Value, gameName,
langPath, langKeyword, ext, projExt,
codeFiles,
GetFiles(langPath, "*.sln", enumerationOptions),
GetFiles(langPath, $"*.{projExt}", enumerationOptions)
);
}
}
@@ -0,0 +1,22 @@
using System.Reflection;
using static System.IO.Directory;
using static DotnetUtils.Globals;
namespace DotnetUtils;
public static class PortInfos {
public static readonly string Root;
static PortInfos() {
Root = GetParent(Assembly.GetEntryAssembly()!.Location)!.FullName;
Root = Root[..Root.IndexOf(@"\00_Utilities")];
Get = GetDirectories(Root)
.SelectMany(gamePath => LangData.Keys.Select(keyword => (gamePath, keyword)))
.SelectT((gamePath, keyword) => PortInfo.Create(gamePath, keyword))
.Where(x => x is not null)
.ToArray()!;
}
public static readonly PortInfo[] Get;
}
@@ -0,0 +1,363 @@
using System.Xml.Linq;
using DotnetUtils;
using static System.Console;
using static System.IO.Path;
using static DotnetUtils.Methods;
using static DotnetUtils.Functions;
var infos = PortInfos.Get;
var actions = new (Action action, string description)[] {
(printInfos, "Output information -- solution, project, and code files"),
(missingSln, "Output missing sln"),
(unexpectedSlnName, "Output misnamed sln"),
(multipleSlns, "Output multiple sln files"),
(missingProj, "Output missing project file"),
(unexpectedProjName, "Output misnamed project files"),
(multipleProjs, "Output multiple project files"),
(checkProjects, "Check .csproj/.vbproj files for target framework, nullability etc."),
(checkExecutableProject, "Check that there is at least one executable project per port"),
(noCodeFiles, "Output ports without any code files"),
(printPortInfo, "Print info about a single port"),
(generateMissingSlns, "Generate solution files when missing"),
(generateMissingProjs, "Generate project files when missing")
};
foreach (var (_, description, index) in actions.WithIndex()) {
WriteLine($"{index}: {description}");
}
WriteLine();
actions[getChoice(actions.Length - 1)].action();
void printSlns(PortInfo pi) {
switch (pi.Slns.Length) {
case 0:
WriteLine("No sln");
break;
case 1:
WriteLine($"Solution: {pi.Slns[0].RelativePath(pi.LangPath)}");
break;
case > 1:
WriteLine("Solutions:");
foreach (var sln in pi.Slns) {
Write(sln.RelativePath(pi.LangPath));
WriteLine();
}
break;
}
}
void printProjs(PortInfo pi) {
switch (pi.Projs.Length) {
case 0:
WriteLine("No project");
break;
case 1:
WriteLine($"Project: {pi.Projs[0].RelativePath(pi.LangPath)}");
break;
case > 1:
WriteLine("Projects:");
foreach (var proj in pi.Projs) {
Write(proj.RelativePath(pi.LangPath));
WriteLine();
}
break;
}
}
void printInfos() {
foreach (var item in infos) {
WriteLine(item.LangPath);
WriteLine();
printSlns(item);
WriteLine();
printProjs(item);
WriteLine();
// get code files
foreach (var file in item.CodeFiles) {
WriteLine(file.RelativePath(item.LangPath));
}
WriteLine(new string('-', 50));
}
}
void missingSln() {
var data = infos.Where(x => x.Slns.None()).ToArray();
foreach (var item in data) {
WriteLine(item.LangPath);
}
WriteLine();
WriteLine($"Count: {data.Length}");
}
void unexpectedSlnName() {
var counter = 0;
foreach (var item in infos) {
if (item.Slns.None()) { continue; }
var expectedSlnName = $"{item.GameName}.sln";
if (item.Slns.Contains(Combine(item.LangPath, expectedSlnName), StringComparer.InvariantCultureIgnoreCase)) { continue; }
counter += 1;
WriteLine(item.LangPath);
WriteLine($"Expected: {expectedSlnName}");
printSlns(item);
WriteLine();
}
WriteLine($"Count: {counter}");
}
void multipleSlns() {
var data = infos.Where(x => x.Slns.Length > 1).ToArray();
foreach (var item in data) {
WriteLine(item.LangPath);
printSlns(item);
}
WriteLine();
WriteLine($"Count: {data.Length}");
}
void missingProj() {
var data = infos.Where(x => x.Projs.None()).ToArray();
foreach (var item in data) {
WriteLine(item.LangPath);
}
WriteLine();
WriteLine($"Count: {data.Length}");
}
void unexpectedProjName() {
var counter = 0;
foreach (var item in infos) {
if (item.Projs.None()) { continue; }
var expectedProjName = $"{item.GameName}.{item.ProjExt}";
if (item.Projs.Contains(Combine(item.LangPath, expectedProjName))) { continue; }
counter += 1;
WriteLine(item.LangPath);
WriteLine($"Expected: {expectedProjName}");
printProjs(item);
WriteLine();
}
WriteLine($"Count: {counter}");
}
void multipleProjs() {
var data = infos.Where(x => x.Projs.Length > 1).ToArray();
foreach (var item in data) {
WriteLine(item.LangPath);
WriteLine();
printProjs(item);
}
WriteLine();
WriteLine($"Count: {data.Length}");
}
void generateMissingSlns() {
foreach (var item in infos.Where(x => x.Slns.None())) {
var result = RunProcess("dotnet", $"new sln -n {item.GameName} -o {item.LangPath}");
WriteLine(result);
var slnFullPath = Combine(item.LangPath, $"{item.GameName}.sln");
foreach (var proj in item.Projs) {
result = RunProcess("dotnet", $"sln {slnFullPath} add {proj}");
WriteLine(result);
}
}
}
void generateMissingProjs() {
foreach (var item in infos.Where(x => x.Projs.None())) {
// We can't use the dotnet command to create a new project using the built-in console template, because part of that template
// is a Program.cs / Program.vb file. If there already are code files, there's no need to add a new empty one; and
// if there's already such a file, it might try to overwrite it.
var projText = item.Lang switch {
"csharp" => @"<Project Sdk=""Microsoft.NET.Sdk"">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>10</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
",
"vbnet" => @$"<Project Sdk=""Microsoft.NET.Sdk"">
<PropertyGroup>
<OutputType>Exe</OutputType>
<RootNamespace>{item.GameName}</RootNamespace>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>16.9</LangVersion>
</PropertyGroup>
</Project>
",
_ => throw new InvalidOperationException()
};
var projFullPath = Combine(item.LangPath, $"{item.GameName}.{item.ProjExt}");
File.WriteAllText(projFullPath, projText);
if (item.Slns.Length == 1) {
var result = RunProcess("dotnet", $"sln {item.Slns[0]} add {projFullPath}");
WriteLine(result);
}
}
}
void checkProjects() {
foreach (var info in infos) {
WriteLine(info.LangPath);
printProjectWarnings(info);
}
}
void printProjectWarnings(PortInfo info) {
foreach (var proj in info.Projs) {
var warnings = new List<string>();
var parent = XDocument.Load(proj).Element("Project")?.Element("PropertyGroup");
var (
framework,
nullable,
implicitUsing,
rootNamespace,
langVersion,
optionStrict
) = (
getValue(parent, "TargetFramework", "TargetFrameworks"),
getValue(parent, "Nullable"),
getValue(parent, "ImplicitUsings"),
getValue(parent, "RootNamespace"),
getValue(parent, "LangVersion"),
getValue(parent, "OptionStrict")
);
if (framework != "net6.0") {
warnings.Add($"Target: {framework}");
}
if (info.Lang == "csharp") {
if (nullable != "enable") {
warnings.Add($"Nullable: {nullable}");
}
if (implicitUsing != "enable") {
warnings.Add($"ImplicitUsings: {implicitUsing}");
}
if (rootNamespace != null && rootNamespace != info.GameName) {
warnings.Add($"RootNamespace: {rootNamespace}");
}
if (langVersion != "10") {
warnings.Add($"LangVersion: {langVersion}");
}
}
if (info.Lang == "vbnet") {
if (rootNamespace != info.GameName) {
warnings.Add($"RootNamespace: {rootNamespace}");
}
if (langVersion != "16.9") {
warnings.Add($"LangVersion: {langVersion}");
}
if (optionStrict != "On") {
warnings.Add($"OptionStrict: {optionStrict}");
}
}
if (warnings.Any()) {
WriteLine(proj.RelativePath(info.LangPath));
WriteLine(string.Join("\n", warnings));
WriteLine();
}
}
}
void checkExecutableProject() {
foreach (var item in infos) {
if (item.Projs.All(proj => getValue(proj, "OutputType") != "Exe")) {
WriteLine($"{item.LangPath}");
}
}
}
void noCodeFiles() {
var qry = infos
.Where(x => x.CodeFiles.None())
.OrderBy(x => x.Lang);
foreach (var item in qry) {
WriteLine(item.LangPath);
}
}
void tryBuild() {
// if has code files, try to build
}
void printPortInfo() {
// prompt for port number
Write("Enter number from 1 to 96 ");
var index = getChoice(1, 96);
Write("Enter 0 for C#, 1 for VB ");
var lang = getChoice(1) switch {
0 => "csharp",
1 => "vbnet",
_ => throw new InvalidOperationException()
};
WriteLine();
var info = infos.Single(x => x.Index == index && x.Lang == lang);
WriteLine(info.LangPath);
WriteLine(new string('-', 50));
// print solutions
printSlns(info);
// mismatched solution name/location? (expected x)
var expectedSlnName = Combine(info.LangPath, $"{info.GameName}.sln");
if (!info.Slns.Contains(expectedSlnName)) {
WriteLine($"Expected name/path: {expectedSlnName.RelativePath(info.LangPath)}");
}
// has executable project?
if (info.Projs.All(proj => getValue(proj, "OutputType") != "Exe")) {
WriteLine("No executable project");
}
WriteLine();
// print projects
printProjs(info);
// mimsatched project name/location? (expected x)
var expectedProjName = Combine(info.LangPath, $"{info.GameName}.{info.ProjExt}");
if (info.Projs.Length < 2 && !info.Projs.Contains(expectedProjName)) {
WriteLine($"Expected name/path: {expectedProjName.RelativePath(info.LangPath)}");
}
WriteLine();
// verify project properties
printProjectWarnings(info);
WriteLine("Code files:");
// list code files
foreach (var codeFile in info.CodeFiles) {
WriteLine(codeFile.RelativePath(info.LangPath));
}
// try build
}
+64
View File
@@ -0,0 +1,64 @@
<NotepadPlus>
<UserLang name="Vintage BASIC" ext="bas" udlVersion="2.1">
<Settings>
<Global caseIgnored="no" allowFoldOfComments="no" foldCompact="no" forcePureLC="0" decimalSeparator="0" />
<Prefix Keywords1="no" Keywords2="no" Keywords3="no" Keywords4="no" Keywords5="no" Keywords6="no" Keywords7="no" Keywords8="no" />
</Settings>
<KeywordLists>
<Keywords name="Comments">00REM 01 02 03 04</Keywords>
<Keywords name="Numbers, prefix1"></Keywords>
<Keywords name="Numbers, prefix2"></Keywords>
<Keywords name="Numbers, extras1"></Keywords>
<Keywords name="Numbers, extras2"></Keywords>
<Keywords name="Numbers, suffix1"></Keywords>
<Keywords name="Numbers, suffix2"></Keywords>
<Keywords name="Numbers, range"></Keywords>
<Keywords name="Operators1">- + ^ * / = &lt;&gt; &lt; &gt; &lt;= &gt;=</Keywords>
<Keywords name="Operators2"></Keywords>
<Keywords name="Folders in code1, open"></Keywords>
<Keywords name="Folders in code1, middle"></Keywords>
<Keywords name="Folders in code1, close"></Keywords>
<Keywords name="Folders in code2, open"></Keywords>
<Keywords name="Folders in code2, middle"></Keywords>
<Keywords name="Folders in code2, close"></Keywords>
<Keywords name="Folders in comment, open"></Keywords>
<Keywords name="Folders in comment, middle"></Keywords>
<Keywords name="Folders in comment, close"></Keywords>
<Keywords name="Keywords1">DATA DEF FN DIM END FOR GOSUB GOTO IF THEN INPUT LET NEXT ON PRINT RANDOMIZE REM RESTORE RETURN STOP TO</Keywords>
<Keywords name="Keywords2">ABS ASC ATN CHR$ COS EXP INT LEFT$ LEN LOG MID$ RIGHT$ RND SGN SIN SPC SQR STR TAB TAN VAL</Keywords>
<Keywords name="Keywords3">NOT AND OR</Keywords>
<Keywords name="Keywords4"></Keywords>
<Keywords name="Keywords5"></Keywords>
<Keywords name="Keywords6"></Keywords>
<Keywords name="Keywords7"></Keywords>
<Keywords name="Keywords8"></Keywords>
<Keywords name="Delimiters">00&quot; 01 02&quot; 03( 04 05) 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23</Keywords>
</KeywordLists>
<Styles>
<WordsStyle name="DEFAULT" fgColor="000000" bgColor="FFFFFF" fontName="Courier New" fontStyle="0" fontSize="14" nesting="0" />
<WordsStyle name="COMMENTS" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="LINE COMMENTS" fgColor="00FF00" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="NUMBERS" fgColor="FF0000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="KEYWORDS1" fgColor="8000FF" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="KEYWORDS2" fgColor="0080C0" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="KEYWORDS3" fgColor="800000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="KEYWORDS4" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="KEYWORDS5" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="KEYWORDS6" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="KEYWORDS7" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="KEYWORDS8" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="OPERATORS" fgColor="800000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="FOLDER IN CODE1" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="FOLDER IN CODE2" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="FOLDER IN COMMENT" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="DELIMITERS1" fgColor="0000FF" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="DELIMITERS2" fgColor="FF8040" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="DELIMITERS3" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="DELIMITERS4" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="DELIMITERS5" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="DELIMITERS6" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="DELIMITERS7" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
<WordsStyle name="DELIMITERS8" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
</Styles>
</UserLang>
</NotepadPlus>
+380
View File
@@ -0,0 +1,380 @@
#!/usr/bin/perl
use strict;
my $Mode= lc($ARGV[0]); #trace #convert
my $File= $ARGV[1];
my $LN= "Line";
my $Pedantic= 0;
my $Indent= 0;
my %Vars; # num | str | anm | ast
my @Data;
my %Code;
open(FH, $File);
while (my $Line = <FH>) {
chomp $Line;
my $Space= index($Line, " ");
my $Key= substr($Line, 0, $Space);
my $Code= substr($Line, $Space+1);
$Code{$Key}=$Code;
}
close(FH);
foreach my $Lin (sort {$a<=>$b} keys %Code) {
if ($Mode eq "trace") { print "==> $Lin $Code{$Lin}\n"; }
my $Ret= &PROCLINE($Code{$Lin});
if ($Mode eq "trace") { print " $Ret\n"; }
$Code{$Lin}= $Ret;
}
if ($Mode eq "convert") {
$Code{'0.1'}= "#!/usr/bin/perl";
$Code{'0.2'}= "#use strict;";
$Code{'0.3'}= "# Automatic converted by bas2perl.pl";
$Code{'0.4'}= "";
foreach my $Lin (sort {$a<=>$b} keys %Code) {
print "$Code{$Lin}\n";
}
}
if (@Data) { &DATAIL(); }
print "\n\n\n";
exit;
sub PROCLINE {
my ($Line)= @_;
my @Sente= &SMARPLIT($Line, ":", "\"");
my $Perl;
foreach my $Sen (@Sente) {
my $Flag=0;
if ($Pedantic==0) {
#REM: Resolves some ugly syntaxis...
$Sen=~ s/\bPRINT"/PRINT "/g; # PRINT"Hello"
$Sen=~ s/"([A-Z])\$/"; $1\$/g; # PRINT "Hello "N$
}
$Sen= &TRIM($Sen);
if ($Sen>0) { $Sen= "GOTO $Sen"; }
if ($Sen=~ /^DATA\b/) { $Sen= &DATA($Sen); $Flag=1; }
if ($Sen=~ /^DIM\b/) { $Sen= &DIM($Sen); $Flag=1; }
if ($Sen=~ /^END\b/) { $Sen= &ENDD($Sen); $Flag=1; }
if ($Sen=~ /^FOR\b/) { $Sen= &FOR($Sen); $Flag=1; }
if ($Sen=~ /^GOTO\b/) { $Sen= &GOTO($Sen); $Flag=1; }
if ($Sen=~ /^GOSUB\b/) { $Sen= &GOSUB($Sen); $Flag=1; }
if ($Sen=~ /^IF\b/) { $Sen= &IF($Sen); $Flag=1; }
if ($Sen=~ /^INPUT\b/) { $Sen= &INPUT($Sen); $Flag=1; }
if ($Sen=~ /^NEXT\b/) { $Sen= &NEXT($Sen); $Flag=1; }
if ($Sen=~ /^ON\b/ && $Sen=~ / GOTO /) { $Sen= &ONGOTO($Sen); $Flag=1; }
if ($Sen=~ /^PRINT\b/) { $Sen= &PRINT($Sen); $Flag=1; }
if ($Sen=~ /^READ\b/) { $Sen= &READ($Sen); $Flag=1; }
if ($Sen=~ /^REM\b/) { $Sen= &REM($Sen); $Flag=1; }
if ($Sen=~ /^RETURN\b/) { $Sen= &RETURN($Sen); $Flag=1; }
if ($Sen=~ /^STOP\b/) { $Sen= &ENDD($Sen); $Flag=1; }
if ($Flag==0) { $Sen= &FORMULA($Sen); } # LET
$Sen.=";";
$Sen=~ s/\{;$/\{/g;
$Sen=~ s/\};$/\}/g;
$Perl.= "$Sen ";
}
$Perl= &TRIM($Perl);
my $Adj= 0;
if ($Perl=~ /^for\b/) { $Adj--; }
if ($Perl eq "}") { $Adj++; }
$Perl= "\t"x ($Indent+$Adj) . $Perl;
return $Perl;
}
####################
# BASIC STATEMENTS #
####################
sub DATA {
my ($Str)= @_;
$Str=~ s/DATA //;
push @Data, $Str;
return "# TO DATA SEGMENT";
}
sub DIM {
my ($Str)= @_;
$Str=~ s/DIM //;
my @Parts= split(/\,(?![^\(]*\))/, $Str);
my $Out;
foreach my $Par (@Parts) {
my $Type= $Par=~ /\$/ ? "ast" : "anm";
$Par=~ s/\$//g;
$Par=~ s/\(.*\)//;
$Vars{$Par}= "anm";
$Out.= "my \@$Par; ";
}
chop $Out;
chop $Out;
return $Out;
}
sub ENDD {
return "exit";
}
sub FOR {
my ($Str)= @_;
$Str=~ s/= /=/g;
my @Parts= split(" ", $Str);
$Parts[1]= &FORMULA($Parts[1]);
my $Var=substr($Parts[1],0,index($Parts[1],"="));
$Parts[3]= "$Var<=".&FORMULA($Parts[3]);
if ($Parts[5]<0) { $Parts[3]=~ s/</>/; }
$Parts[5]= $Parts[5] eq "" ? "$Var++" : "$Var+=".&FORMULA($Parts[5]);
$Str= "for ($Parts[1]; $Parts[3]; $Parts[5]) {";
$Indent++;
return $Str;
}
sub GOTO {
# The birth of spaguetti code!
# Dijkstra would not like this...
my ($Str)= @_;
my @Parts= split(" ", $Str);
my $Label= "$LN$Parts[1]";
$Str= lc($Parts[0])." $Label";
$Code{($Parts[1]-.2)}="";
$Code{($Parts[1]-.1)}="$Label:";
return $Str;
}
sub GOSUB {
my ($Str)= @_;
my @Parts= split(" ", $Str);
my $Label= "$LN$Parts[1]";
$Str= "\&$Label()";
$Code{($Parts[1]-.2)}="";
$Code{($Parts[1]-.1)}="sub $Label {";
return $Str;
}
sub IF {
my ($Str)= @_;
$Str=~ s/^IF //g;
my @Parts= split(" THEN ", $Str);
$Parts[0]= &FORMULA($Parts[0], 1);
$Parts[1]= &PROCLINE($Parts[1]);
my $Str= "if ($Parts[0]) { $Parts[1] }";
return $Str;
}
sub INPUT {
my ($Str)= @_;
$Str=~ s/INPUT //;
$Str=~ s/"(.*)"//g;
my $Txt= qq|print "$1\? "; |;
my @Parts= split(/,/, $Str);
my @Multi;
foreach my $Par (@Parts) {
my $Type= "num";
if ($Par=~ /\$/) {
$Type= "str";
$Par=~ s/\$//g;
}
if ($Par=~ /\(/) {
if ($Type eq "num") { $Type= "anm"; }
if ($Type eq "str") { $Type= "ast"; }
$Par=~ s/\(/\[/g;
$Par=~ s/\)/\]/g;
}
$Par=~ s/\;//g;
push @Multi, "\$$Par";
my $Name= $Par;
$Name=~ s/\[.*\]//;
$Vars{$Name}= $Type;
}
$Str= join(",", @Multi);
my $Spl= "";
if (scalar @Parts>1) {
$Spl= "; ($Str)= split(/,/, \$Inp_)";
$Str= "\$Inp_";
}
my $Inp= qq|chomp($Str = uc(<STDIN>))$Spl|;
if ($Str=~ /,/) {
$Str= "\$$Str";
$Str=~ s/,/,\$/g;
$Str= "Inp";
}
return $Txt.$Inp;
}
sub NEXT {
$Indent--;
return "}";
}
sub ONGOTO {
# Base 1, if not match it will be skipped.
my ($Str)= @_;
my @Parts= split(" ", $Str);
my $Var= $Parts[1];
my @Lines= split(",", $Parts[3]);
my $Count=0;
my $Text;
foreach my $Lin (@Lines) {
$Count++;
my $This= "\telsif (\$$Var==$Count) ";
if ($Count==1) { $This= "if (\$$Var==1) "; }
my $Goto= &GOTO("GOTO $Lin");
$This.="{ $Goto; }\n";
$Text.= $This;
}
return $Text;
}
sub PRINT {
my ($Str)= @_;
if ($Str eq "PRINT") { return 'print "\n"' };
$Str=~ s/^PRINT //;
my $Enter= 1;
if ($Str=~ /;$/) { $Enter= 0; }
my @Parts= &SMARPLIT($Str, ";", "\"");
my @Out;
foreach my $Par (@Parts) {
if ($Par=~ /"/) {
push @Out, $Par;
next;
}
if ($Par=~ /TAB\((.*?)\)/) {
push @Out, "' 'x".&FORMULA($1)." ";
next;
}
$Par= &FORMULA($Par);
push @Out, $Par;
}
my $Out= join(". ", @Out);
if ($Enter) { $Out.= qq|. "\\n"|; }
return "print ".$Out;
}
sub READ {
my ($Str)= @_;
$Str=~ s/READ //;
$Str= &FORMULA($Str);
$Str.="= <DATA>; chomp($Str)";
return $Str;
}
sub REM {
my ($Str)= @_;
return "#".$Str;
}
sub RETURN {
return "return; }";
}
###########
# HELPERS #
###########
sub TRIM {
my ($Str)= @_;
#$Str=~ s/\s+/ /g;
$Str=~ s/^\s+//;
$Str=~ s/\s+$//;
return $Str;
}
sub DATAIL {
print "\n\n\n";
print "__DATA__\n";
foreach my $Dat (@Data) {
$Dat=~ s/"//g;
$Dat=~ s/,/\n/g;
print "$Dat\n";
}
}
sub FORMULA {
my ($Str, $Cond)= @_;
$Str=~ s/\$//g;
$Str=~ s/ABS\(/abs\(/g;
$Str=~ s/COS\(/cos\(/g;
$Str=~ s/LEN\(/length\(/g;
$Str=~ s/INT\(/int\(/g;
$Str=~ s/MID\$?\(/substr\(/g;
$Str=~ s/RND\(/rand\(/g;
$Str=~ s/SIN\(/sin\(/g;
$Str=~ s/SQR\(/sqr\(/g;
$Str=~ s/(\b[A-Z][0-9]?\b)/\$$&/g;
#==> Check for arrays...
foreach my $Key (keys %Vars) {
if ($Vars{$Key}!~ /^a/) { next; }
$Str=~ s/\$$Key\((.*?)\)/\$$Key\[$1\]/g;
}
if ($Cond==1) {
$Str=~ s/<>/ ne /g;
$Str=~ s/=/ eq /g;
}
return $Str;
}
sub SMARPLIT {
my ($Str, $Sep, $Nin)= @_;
my @Parts;
my $Text= "";
my $Flag= 0;
my $Prev;
foreach my $Char (split('', $Str)) {
if ($Char eq $Nin) { $Flag= !$Flag; }
if ($Char eq $Sep && $Flag==0) {
push @Parts, &TRIM($Text);
$Text= "";
next;
}
$Prev= $Char;
$Text.= $Char;
}
if ($Text) { push @Parts, &TRIM($Text); }
return @Parts;
}
@@ -0,0 +1,84 @@
/**
* Program to find games that are missing solutions in a given language
*
* Scan each game folder, check for a folder for each language, and also make
* sure there's at least one file of the expected extension and not just a
* readme or something
*/
const fs = require("fs");
const glob = require("glob");
// relative path to the repository root
const ROOT_PATH = "../.";
const languages = [
{ name: "csharp", extension: "cs" },
{ name: "java", extension: "java" },
{ name: "javascript", extension: "html" },
{ name: "pascal", extension: "pas" },
{ name: "perl", extension: "pl" },
{ name: "python", extension: "py" },
{ name: "ruby", extension: "rb" },
{ name: "vbnet", extension: "vb" },
];
const getFilesRecursive = async (path, extension) => {
return new Promise((resolve, reject) => {
glob(`${path}/**/*.${extension}`, (err, matches) => {
if (err) {
reject(err);
}
resolve(matches);
});
});
};
const getPuzzleFolders = () => {
return fs
.readdirSync(ROOT_PATH, { withFileTypes: true })
.filter((dirEntry) => dirEntry.isDirectory())
.filter(
(dirEntry) =>
![".git", "node_modules", "00_Utilities"].includes(dirEntry.name)
)
.map((dirEntry) => dirEntry.name);
};
(async () => {
let missingGames = {};
let missingLanguageCounts = {};
languages.forEach((l) => (missingLanguageCounts[l.name] = 0));
const puzzles = getPuzzleFolders();
for (const puzzle of puzzles) {
for (const { name: language, extension } of languages) {
const files = await getFilesRecursive(
`${ROOT_PATH}/${puzzle}/${language}`,
extension
);
if (files.length === 0) {
if (!missingGames[puzzle]) missingGames[puzzle] = [];
missingGames[puzzle].push(language);
missingLanguageCounts[language]++;
}
}
}
const missingCount = Object.values(missingGames).flat().length;
if (missingCount === 0) {
console.log("All games have solutions for all languages");
} else {
console.log(`Missing ${missingCount} implementations:`);
Object.entries(missingGames).forEach(
([p, ls]) => (missingGames[p] = ls.join(", "))
);
console.log(`\nMissing languages by game:`);
console.table(missingGames);
console.log(`\nBy language:`);
console.table(missingLanguageCounts);
}
})();
return;
+73
View File
@@ -0,0 +1,73 @@
/**
* Program to show unimplemented games by language, optionally filtered by
* language
*
* Usage: node find-unimplemented.js [[[lang1] lang2] ...]
*
* Adapted from find-missing-implementtion.js
*/
const fs = require("fs");
const glob = require("glob");
// relative path to the repository root
const ROOT_PATH = "../.";
let languages = [
{ name: "csharp", extension: "cs" },
{ name: "java", extension: "java" },
{ name: "javascript", extension: "html" },
{ name: "pascal", extension: "pas" },
{ name: "perl", extension: "pl" },
{ name: "python", extension: "py" },
{ name: "ruby", extension: "rb" },
{ name: "vbnet", extension: "vb" },
];
const getFilesRecursive = async (path, extension) => {
return new Promise((resolve, reject) => {
glob(`${path}/**/*.${extension}`, (err, matches) => {
if (err) {
reject(err);
}
resolve(matches);
});
});
};
const getPuzzleFolders = () => {
return fs
.readdirSync(ROOT_PATH, { withFileTypes: true })
.filter((dirEntry) => dirEntry.isDirectory())
.filter(
(dirEntry) =>
![".git", "node_modules", "00_Utilities", "buildJvm"].includes(dirEntry.name)
)
.map((dirEntry) => dirEntry.name);
};
(async () => {
const result = {};
if (process.argv.length > 2) {
languages = languages.filter((language) => process.argv.slice(2).includes(language.name));
}
for (const { name: language } of languages) {
result[language] = [];
}
const puzzleFolders = getPuzzleFolders();
for (const puzzleFolder of puzzleFolders) {
for (const { name: language, extension } of languages) {
const files = await getFilesRecursive(
`${ROOT_PATH}/${puzzleFolder}/${language}`, extension
);
if (files.length === 0) {
result[language].push(puzzleFolder);
}
}
}
console.log('Unimplementation by language:')
console.dir(result);
})();
return;
@@ -0,0 +1,37 @@
package com.pcholt.console.testutils
import com.google.common.truth.Truth
import org.junit.Rule
import org.junit.contrib.java.lang.system.SystemOutRule
import org.junit.contrib.java.lang.system.TextFromStandardInputStream
abstract class ConsoleTest {
@get:Rule
val inputRule = TextFromStandardInputStream.emptyStandardInputStream()
@get:Rule
val systemOutRule = SystemOutRule().enableLog()
val regexInputCommand = "\\{(.*)}".toRegex()
fun assertConversation(conversation: String, runMain: () -> Unit) {
inputRule.provideLines(*regexInputCommand
.findAll(conversation)
.map { it.groupValues[1] }
.toList().toTypedArray())
runMain()
Truth.assertThat(
systemOutRule.log.trimWhiteSpace()
)
.isEqualTo(
regexInputCommand
.replace(conversation, "").trimWhiteSpace()
)
}
private fun String.trimWhiteSpace() =
replace("[\\s]+".toRegex(), " ")
}
+2 -2
View File
@@ -18,7 +18,7 @@ checklist = ["game", "csharp", "java", "javascript",
prev_game = ""
for dirName, subdirList, fileList in os.walk(rootDir):
split_dir = dirName.split("/")
split_dir = dirName.split(os.path.sep)
if len(split_dir) == 2 and not split_dir[1] in ['.git', '00_Utilities']:
if prev_game == "":
@@ -46,5 +46,5 @@ sorted_strings = list(map(lambda l: " | ".join(l) + "\n",
write_string += ''.join(sorted_strings)
with open("README.md", "w") as f:
with open("README.md", "w", encoding='utf-8') as f:
f.write(write_string)
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/perl
#YATOL: Yet Another TOdo List
use strict;
#REM: Get list of basic files ordered by number of lines.
#REM: This way you can do the easier ones first.
my @Ret=`find .. -iname '*.bas' -exec wc -l \{\} \\; | sort -h`;
my @Langs= qw(PL JS VB PAS RB C# JAVA PY);
my @Dirs= qw(perl javascript vbnet pascal ruby csharp java python);
my %Sum;
my $Row=25;
my $Tab=7;
printf("%-$Row\s", "PATH");
printf("%$Tab\s", "BAS");
foreach my $Dir (@Langs) {
printf("%$Tab\s", $Dir);
}
print "\n";
my $Count;
foreach my $Lin (@Ret) {
$Count++;
chomp $Lin;
my ($Num, $File)= split (" ", $Lin);
my @Parts= split(/\//, $File);
my $Base= $Parts[1];
printf("%-$Row\s", $Base);
printf("%$Tab\s", "$Num");
foreach my $Dir (@Dirs) {
my $Path= "../$Base/$Dir/";
my $Ret= `ls $Path | wc -l`;
if ($Ret>1) { printf("%$Tab\s", "YES"); $Sum{$Dir}++; }
else { printf("%$Tab\s", " ");}
}
print "\n";
}
printf("%$Row\s", "FILES:");
printf("%$Tab\s", " ");
foreach my $Dir (@Dirs) {
printf("%$Tab\s", "$Sum{$Dir}");
}
print "\n";
printf("%$Row\s", "ADVANCE:");
printf("%$Tab\s", " ");
foreach my $Dir (@Dirs) {
my $Per= int($Sum{$Dir}/$Count*100)."%";
printf("%$Tab\s", "$Per");
}
print "\n";
+1
View File
@@ -17,3 +17,4 @@ http://www.vintage-basic.net/games.html
#### External Links
- Common Lisp: https://github.com/koalahedron/lisp-computer-games/blob/master/01%20Acey%20Ducey/common-lisp/acey-deucy.lisp
- PowerShell: https://github.com/eweilnau/basic-computer-games-powershell/blob/main/AceyDucey.ps1
+6
View File
@@ -1,3 +1,9 @@
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
Conversion to [Oracle Java](https://openjdk.java.net/)
Two versions of Acey Ducey have been contributed.
The original upload supported JDK 8/JDK 11 and uses multiple files and the second uses features in JDK 17 and is implemented in a single file AceyDucey17.java.
Both are in the src folder.
+201
View File
@@ -0,0 +1,201 @@
import java.util.Random;
import java.util.Scanner;
/**
* A modern version (JDK17) of ACEY DUCEY using post Java 8 features. Notes
* regarding new java features or differences in the original basic
* implementation are numbered and at the bottom of this code.
* The goal is to recreate the exact look and feel of the original program
* minus a large glaring bug in the original code that lets you cheat.
*/
public class AceyDucey17 {
public static void main(String[] args) {
// notes [1]
System.out.println("""
ACEY DUCEY CARD GAME
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER
THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP
YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING
ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE
A VALUE BETWEEN THE FIRST TWO.
IF YOU DO NOT WANT TO BET, INPUT A 0""");
do {
playGame();
} while (stillInterested());
System.out.println("O.K., HOPE YOU HAD FUN!");
}
public static void playGame() {
int cashOnHand = 100; // our only mutable variable note [11]
System.out.println("YOU NOW HAVE "+ cashOnHand +" DOLLARS.");// note [6]
while (cashOnHand > 0) {
System.out.println();
System.out.println("HERE ARE YOUR NEXT TWO CARDS:");
final Card lowCard = Card.getRandomCard(2, Card.KING); //note [3]
System.out.println(lowCard);
final Card highCard = Card.getRandomCard(lowCard.rank() + 1, Card.ACE);
System.out.println(highCard);
final int bet = getBet(cashOnHand);
final int winnings = determineWinnings(lowCard,highCard,bet);
cashOnHand += winnings;
if(winnings != 0 || cashOnHand != 0){ //note [2]
System.out.println("YOU NOW HAVE "+ cashOnHand +" DOLLARS.");//note [6]
}
}
}
public static int determineWinnings(Card lowCard, Card highCard, int bet){
if (bet <= 0) { // note [5]
System.out.println("CHICKEN!!");
return 0;
}
Card nextCard = Card.getRandomCard(2, Card.ACE);
System.out.println(nextCard);
if(nextCard.between(lowCard,highCard)){
System.out.println("YOU WIN!!!");
return bet;
}
System.out.println("SORRY, YOU LOSE");
return -bet;
}
public static boolean stillInterested(){
System.out.println();
System.out.println();
System.out.println("SORRY, FRIEND, BUT YOU BLEW YOUR WAD.");
System.out.println();
System.out.println();
System.out.print("TRY AGAIN (YES OR NO)? ");
Scanner input = new Scanner(System.in);
boolean playAgain = input.nextLine()
.toUpperCase()
.startsWith("Y"); // note [9]
System.out.println();
System.out.println();
return playAgain;
}
public static int getBet(int cashOnHand){
int bet;
do{
System.out.println();
System.out.print("WHAT IS YOUR BET? ");
bet = inputNumber();
if (bet > cashOnHand) {
System.out.println("SORRY, MY FRIEND, BUT YOU BET TOO MUCH.");
System.out.println("YOU HAVE ONLY "+cashOnHand+" DOLLARS TO BET.");
}
}while(bet > cashOnHand);
return bet;
}
public static int inputNumber() {
final Scanner input = new Scanner(System.in);
// set to negative to mark as not entered yet in case of input error.
int number = -1;
while (number < 0) {
try {
number = input.nextInt();
} catch(Exception ex) { // note [7]
System.out.println("!NUMBER EXPECTED - RETRY INPUT LINE");
System.out.print("? ");
try{
input.nextLine();
}
catch(Exception ns_ex){ // received EOF (ctrl-d or ctrl-z if windows)
System.out.println("END OF INPUT, STOPPING PROGRAM.");
System.exit(1);
}
}
}
return number;
}
record Card(int rank){
// Some constants to describe face cards.
public static final int JACK = 11, QUEEN = 12, KING = 13, ACE = 14;
private static final Random random = new Random();
public static Card getRandomCard(int from, int to){
return new Card(random.nextInt(from, to+1)); // note [4]
}
public boolean between(Card lower, Card higher){
return lower.rank() < this.rank() && this.rank() < higher.rank();
}
@Override
public String toString() { // note [13]
return switch (rank) {
case JACK -> "JACK";
case QUEEN -> "QUEEN";
case KING -> "KING";
case ACE -> "ACE\n"; // note [10]
default -> " "+rank+" "; // note [6]
};
}
}
/*
Notes:
1. Multiline strings, a.k.a. text blocks, were added in JDK15.
2. The original game only displays the players balance if it changed,
which it does not when the player chickens out and bets zero.
It also doesn't display the balance when it becomes zero because it has
a more appropriate message: Sorry, You Lose.
3. To pick two cards to show, the original BASIC implementation has a
bug that could cause a race condition if the RND function never chose
a lower number first and higher number second. It loops infinitely
re-choosing random numbers until the condition is met of the first
one being lower. The logic is changed a bit here so that the first
card picked is anything but an ACE, the highest possible card,
and then the second card is between the just picked first card upto
and including the ACE.
4. Random.nextInt(origin, bound) was added in JDK17, and allows to
directly pick a range for a random integer to be generated. The second
parameter is exclusive of the range and thus why they are stated with
+1's to the face card.
5. The original BASIC implementation has a bug that allows negative value
bets. Since you can't bet MORE cash than you have you can always bet
less including a very, very large negative value. You would do this when
the chances of winning are slim or zero since losing a hand SUBTRACTS
your bet from your cash; subtracting a negative number actually ADDS
to your cash, potentially making you an instant billionaire.
This loophole is now closed.
6. The subtle behavior of the BASIC PRINT command causes a space to be
printed before all positive numbers as well as a trailing space. Any
place a non-face card or the players balance is printed has extra space
to mimic this behavior.
7. Errors on input were probably specific to the interpreter. This program
tries to match the Vintage Basic interpreter's error messages. The final
input.nextLine() command exists to clear the blockage of whatever
non-number input was entered. But even that could fail if the user
types Ctrl-D (windows Ctrl-Z), signifying an EOF (end of file) and thus
the closing of STDIN channel. The original program on an EOF signal prints
"END OF INPUT IN LINE 660" and thus we cover it roughly the same way.
All of this is necessary to avoid a messy stack trace from being
printed as the program crashes.
9. The original game only accepted a full upper case "YES" to continue
playing if bankrupted. This program is more lenient and will accept
any input that starts with the letter 'y', uppercase or not.
10. The original game prints an extra blank line if the card is an ACE. There
is seemingly no rationale for this.
11. Modern java best practices are edging toward a more functional paradigm
and as such, mutating state is discouraged. All other variables besides
the cashOnHand are final and initialized only once.
12. Refactoring of the concept of a card is done with a record. Records were
introduced in JDK14. Card functionality is encapsulated in this example
of a record. An enum could be a better alternative since there are
technically only 13 cards possible.
13. Switch expressions were introduced as far back as JDK12 but continue to
be refined for clarity, exhaustiveness. As of JDK17 pattern matching
for switch expressions can be accessed by enabling preview features.
*/
}
+74
View File
@@ -0,0 +1,74 @@
import java.util.Random
fun printCard(a: Int) {
if (a < 11) println(a)
if (a == 11) println("JACK")
if (a == 12) println("QUEEN")
if (a == 13) println("KING")
if (a == 14) println("ACE")
}
fun main() {
println("ACEY DUCEY CARD GAME")
println("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")
println()
println()
println("ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER ")
println("THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP")
println("YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING")
println("ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE")
println("A VALUE BETWEEN THE FIRST TWO.")
println("IF YOU DO NOT WANT TO BET, INPUT A 0")
var random = Random()
do {
var q = 100
var a : Int
var b : Int
var m : Int
println("YOU NOW HAVE " + q + " DOLLARS.")
println()
do {
do {
do {
println("HERE ARE YOUR NEXT TWO CARDS: ")
do {
a = random.nextInt(12) + 2
b = random.nextInt(12) + 2
} while (a >= b);
printCard(a)
printCard(b)
println()
println()
print("WHAT IS YOUR BET")
m = readLine()!!.toInt()
if (m == 0) {
println("CHICKEN!!")
println()
}
} while (m == 0);
if (m > q) {
println("SORRY, MY FRIEND, BUT YOU BET TOO MUCH.")
println("YOU HAVE ONLY " + q + " DOLLARS TO BET.")
}
} while (m > q);
var c = random.nextInt(12) + 2
printCard(c)
println()
if (c > a && c < b) {
println("YOU WIN!!!")
q += m
}
else {
println("SORRY, YOU LOSE")
if (m < q) q -= m
}
} while (m < q);
println()
println()
println("SORRY, FRIEND, BUT YOU BLEW YOUR WAD.")
println()
println()
println("TRY AGAIN (YES OR NO)")
} while (readLine() == "YES");
println("O.K., HOPE YOU HAD FUN!")
}
+9
View File
@@ -0,0 +1,9 @@
[package]
name = "rust"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rand = "0.8.5"
+7
View File
@@ -0,0 +1,7 @@
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
Conversion to [Rust](https://www.rust-lang.org/) by Alex Kotov [mur4ik18@github](https://github.com/mur4ik18).
Further edits by
- Berker Şal [berkersal@github](https://github.com/berkersal)
+139
View File
@@ -0,0 +1,139 @@
use rand::{prelude::ThreadRng, Rng};
use std::{fmt, io, mem};
#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct Card(u8);
impl Card {
fn new_random(rng: &mut ThreadRng) -> Card {
Card(rng.gen_range(2..15))
}
}
impl fmt::Display for Card {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self.0 {
11 => String::from("JACK"),
12 => String::from("QUEEN"),
13 => String::from("KING"),
14 => String::from("ACE"),
otherwise => otherwise.to_string(),
}
)
}
}
struct CardsPool(Card, Card, Card);
impl CardsPool {
fn new() -> CardsPool {
let mut rng = rand::thread_rng();
let mut first = Card::new_random(&mut rng);
let mut second = Card::new_random(&mut rng);
let third = Card::new_random(&mut rng);
if first > second {
mem::swap(&mut first, &mut second);
}
CardsPool(first, second, third)
}
fn is_in_win_range(&self) -> bool {
self.0 <= self.2 && self.2 <= self.1
}
}
fn main() {
hello();
// user start bank
let mut user_bank: u16 = 100;
loop {
println!("YOU NOW HAVE {} DOLLARS.", &mut user_bank);
println!("\nHERE ARE YOUR NEXT TWO CARDS:");
// get new random cards
let cards = CardsPool::new();
println!("{}", cards.0);
println!("{}", cards.1);
let user_bet: u16 = get_bet(user_bank);
if user_bet == 0 {
println!("CHICKEN!!!\n");
continue;
} else {
println!("THANK YOU! YOUR BET IS {} DOLLARS.", user_bet);
}
println!("\nTHE THIRD CARD IS:");
println!("{}", cards.2);
if cards.is_in_win_range() {
println!("\nYOU WIN!!!");
user_bank += user_bet;
} else {
println!("\nSORRY, YOU LOSE");
user_bank -= user_bet;
}
if user_bank == 0 {
println!("\nSORRY, FRIEND, BUT YOU BLEW YOUR WAD.");
println!("\nTRY AGAIN? (YES OR NO)");
let mut input = String::new();
io::stdin().read_line(&mut input).expect("Incorrect input");
if input.trim().to_lowercase() == "yes" {
user_bank = 100;
println!();
} else {
println!("\nO.K., HOPE YOU HAD FUN!");
break;
}
}
}
}
fn hello() {
println!(" 🂡 ACEY DUCEY CARD GAME 🂱");
println!("CREATIVE COMPUTING - MORRISTOWN, NEW JERSEY");
println!("ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER");
println!("THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP");
println!("YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING");
println!("ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE");
println!("A VALUE BETWEEN THE FIRST TWO.");
println!("IF YOU DO NOT WANT TO BET IN A ROUND, ENTER 0");
println!("\n\n");
}
fn get_bet(user_bank: u16) -> u16 {
loop {
println!("\nWHAT IS YOUR BET? ENTER 0 IF YOU DON'T WANT TO BET (CTRL+C TO EXIT)");
let bet: u16;
let mut input = String::new();
io::stdin()
.read_line(&mut input)
.expect("CANNOT READ INPUT!");
match input.trim().parse::<u16>() {
Ok(i) => bet = i,
Err(e) => {
println!("CHECK YOUR INPUT! {}!", e.to_string().to_uppercase());
continue;
}
};
match bet {
bet if bet <= user_bank => return bet,
_ => {
println!("\nSORRY, MY FRIEND, BUT YOU BET TOO MUCH.");
println!("YOU HAVE ONLY {} DOLLARS TO BET.", user_bank);
}
};
}
}
@@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "AceyDucy", "AceyDucy\AceyDucy.vbproj", "{37496710-B458-4502-ADCB-4C57203866F9}"
Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "AceyDucey", "AceyDucey.vbproj", "{54C05475-238D-4A82-A67B-B6C1BF2CA337}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -11,10 +11,10 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{37496710-B458-4502-ADCB-4C57203866F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{37496710-B458-4502-ADCB-4C57203866F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{37496710-B458-4502-ADCB-4C57203866F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{37496710-B458-4502-ADCB-4C57203866F9}.Release|Any CPU.Build.0 = Release|Any CPU
{54C05475-238D-4A82-A67B-B6C1BF2CA337}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{54C05475-238D-4A82-A67B-B6C1BF2CA337}.Debug|Any CPU.Build.0 = Debug|Any CPU
{54C05475-238D-4A82-A67B-B6C1BF2CA337}.Release|Any CPU.ActiveCfg = Release|Any CPU
{54C05475-238D-4A82-A67B-B6C1BF2CA337}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+242
View File
@@ -0,0 +1,242 @@
Public Class AceyDucey
''' <summary>
''' Create a single instance of the Random class to be used
''' throughout the program.
''' </summary>
Private ReadOnly Property Rnd As New Random()
''' <summary>
''' Define a varaible to store the the player balance. <br/>
''' Defaults to 0
''' </summary>
''' <remarks>
''' Since <see cref="Integer"/> is a value type, and no value
''' has been explicitly set, the default value of the type is used.
''' </remarks>
Private _balance As Integer
Public Sub New()
DisplayIntroduction()
End Sub
''' <summary>
''' Play multiple games of Acey Ducey until the player chooses to quit.
''' </summary>
Public Sub Play()
Do
PlayGame()
Loop While TryAgain() 'Loop (play again) based on the Boolean value returned by TryAgain
Console.WriteLine("O.K., HOPE YOU HAD FUN!")
End Sub
''' <summary>
''' Play a game of Acey Ducey, which ends when the player balance reaches 0
''' </summary>
Private Sub PlayGame()
_balance = 100 'At the start of the game, set the player balance to 100
Console.WriteLine()
Console.WriteLine($"YOU NOW HAVE {_balance} DOLLARS.")
Do
PlayTurn()
Loop While _balance > 0 'Continue playing while the user has a balance
Console.WriteLine()
Console.WriteLine("SORRY, FRIEND, BUT YOU BLEW YOUR WAD.")
End Sub
''' <summary>
''' Play one turn of Acey Ducey
''' </summary>
''' <remarks>
''' A turn consists of displaying to cards, making a wager
''' and determining the result (win/lose)
''' </remarks>
Private Sub PlayTurn()
Console.WriteLine()
Console.WriteLine("HERE ARE YOUR NEXT TWO CARDS: ")
Dim cards = GetOrderedCards()
For Each card In cards
DisplayCard(card)
Next
Dim wager As Integer = GetWager()
Dim finalCard As Integer = GetCard()
If wager = 0 Then
Console.WriteLine("CHICKEN!!")
Return
End If
DisplayCard(finalCard)
Console.WriteLine()
'''Check if the value of the final card is between the first and second cards.
'''
'''The use of AndAlso is used to short-circuit the evaluation of the IF condition.
'''Short-circuiting means that both sides of the condition do not need to be
'''evaluated. In this case, if the left criteria returns FALSE, the right criteria
'''is ignored and the evaluation result is returned as FALSE.
'''
'''This works because AndAlso requires both condition to return TRUE in order to be
'''evaluated as TRUE. If the first condition is FALSE we already know the evaluation result.
If finalCard >= cards.First() AndAlso finalCard <= cards.Last() Then
Console.WriteLine("YOU WIN!!!")
_balance += wager 'Condensed version of _balance = _balance + wager
Else
Console.WriteLine("SORRY, YOU LOSE.")
_balance -= wager 'Condensed version of _balance = _balance - wager
End If
End Sub
''' <summary>
''' Get two cards in ascending order
''' </summary>
''' <remarks>
''' The original version generates two cards (A and B)
''' If A is greater than or equal to B, both cards are regenerated.
''' <br/><br/>
''' This version generates the two cards, but only regenerates A
''' if A is equal to B. The cards are then returned is ascending order,
''' ensuring that A is less than B (maintaining the original end result)
''' </remarks>
Private Function GetOrderedCards() As Integer()
'''When declaring fixed size arrays in VB.NET you declare the MAX INDEX of the array
'''and NOT the SIZE (number of elements) of the array.
'''As such, card(1) gives you and array with index 0 and index 1, which means
'''the array stores two elements and not one
Dim cards(1) As Integer
cards(0) = GetCard()
cards(1) = GetCard()
'Perform this action as long as the first card is equal to the second card
While cards(0) = cards(1)
cards(0) = GetCard()
End While
Array.Sort(cards) 'Sort the values in ascending order
Return cards
End Function
''' <summary>
''' Get a random number (card) ranked 2 to 14
''' </summary>
Private Function GetCard() As Integer
Return Rnd.Next(2, 15)
End Function
''' <summary>
''' Display the face value of the card
''' </summary>
Private Sub DisplayCard(card As Integer)
Dim output As String
Select Case card
Case 2 To 10
output = card.ToString()
Case 11
output = "JACK"
Case 12
output = "QUEEN"
Case 13
output = "KING"
Case 14
output = "ACE"
Case Else
Throw New ArgumentOutOfRangeException(NameOf(card), "Value must be between 2 and 14")
End Select
Console.WriteLine(output)
End Sub
''' <summary>
''' Prompt the user to make a bet
''' </summary>
''' <remarks>
''' The function will not return until a valid bet is made. <br/>
''' <see cref="Int32.TryParse(String, ByRef Integer)"/> is used to validate that the user input is a valid <see cref="Integer"/>
''' </remarks>
Private Function GetWager() As Integer
Dim wager As Integer
Do
Console.WriteLine()
Console.Write("WHAT IS YOUR BET? ")
Dim input As String = Console.ReadLine()
'''Determine if the user input is an Integer
'''If it is an Integer, store the value in the variable wager
If Not Integer.TryParse(input, wager) Then
Console.WriteLine("SORRY, I DID'T QUITE GET THAT.")
Continue Do 'restart the loop
End If
'Prevent the user from betting more than their current balance
If _balance < wager Then
Console.WriteLine("SORRY, MY FRIEND, BUT YOU BET TOO MUCH.")
Console.WriteLine($"YOU HAVE ONLY {_balance} DOLLARS TO BET.")
Continue Do 'restart the loop
End If
'Prevent the user from betting negative values
If wager < 0 Then
Console.WriteLine("FUNNY GUY! YOU CANNOT MAKE A NEGATIVE BET.")
Continue Do 'restart the loop
End If
Exit Do 'If we get to this line, exit the loop as all above validations passed
Loop
Return wager
End Function
''' <summary>
''' Prompt the user to try again
''' </summary>
''' <remarks>
''' This function will not return until a valid reponse is given
''' </remarks>
Private Function TryAgain() As Boolean
Dim response As String
Do
Console.Write("TRY AGAIN (YES OR NO) ")
response = Console.ReadLine()
If response.Equals("YES", StringComparison.OrdinalIgnoreCase) Then Return True
If response.Equals("NO", StringComparison.OrdinalIgnoreCase) Then Return False
Console.WriteLine("SORRY, I DID'T QUITE GET THAT.")
Loop
End Function
''' <summary>
''' Display the opening title and instructions
''' </summary>
''' <remarks>
''' Refer to
''' <see href="https://docs.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/strings/interpolated-strings">
''' Interpolated Strings
''' </see> documentation for the use of $ and { } with strings
''' </remarks>
Private Sub DisplayIntroduction()
Console.WriteLine($"{Space((Console.WindowWidth \ 2) - 10)}ACEY DUCEY CARD GAME")
Console.WriteLine($"{Space((Console.WindowWidth \ 2) - 21)}CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")
Console.WriteLine("")
Console.WriteLine("")
Console.WriteLine("ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER")
Console.WriteLine("THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP")
Console.WriteLine("YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING")
Console.WriteLine("ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE")
Console.WriteLine("A VALUE BETWEEN THE FIRST TWO.")
Console.WriteLine("IF YOU DO NOT WANT TO BET, INPUT A 0")
Console.WriteLine("")
End Sub
End Class
+10
View File
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<RootNamespace>AceyDucey</RootNamespace>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>16.9</LangVersion>
</PropertyGroup>
</Project>
-178
View File
@@ -1,178 +0,0 @@
Imports System
''' <summary>
''' This is a modern adapation of Acey Ducey from BASIC Computer Games.
'''
''' The structural changes primarily consist of replacing the many GOTOs with
''' Do/Loop constructs to force the continual execution of the program.
'''
''' Because modern Basic allows multi-line If/Then blocks, many GOTO jumps were
''' able to be eliminated and the logic was able to be moved to more relevant areas,
''' For example, the increment/decrement of the player's balance could be in the same
''' area as the notification of win/loss.
'''
''' Some modern improvements were added, primarily the inclusion of a function, which
''' eliminated a thrice-repeated block of logic to display the card value. The archaic
''' RND function is greatly simplified with the .NET Framework's Random class.
'''
''' Elementary comments are provided for non-programmers or novices.
''' </summary>
Module Program
Sub Main(args As String())
' These are the variables that will hold values during the program's execution
Dim input As String
Dim rnd As New Random ' You can create a new instance of an object during declaration
Dim currentBalance As Integer = 100 ' You can set a initial value at declaration
Dim currentWager As Integer
Dim cardA, cardB, cardC As Integer ' You can specify multiple variables of the same type in one declaration statement
' Display the opening title and instructions
' Use a preceding $ to insert calculated values within the string using {}
Console.WriteLine($"{Space((Console.WindowWidth \ 2) - 10)}ACEY DUCEY CARD GAME")
Console.WriteLine($"{Space((Console.WindowWidth \ 2) - 21)}CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")
Console.WriteLine("")
Console.WriteLine("")
Console.WriteLine("ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER")
Console.WriteLine("THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP")
Console.WriteLine("YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING")
Console.WriteLine("ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE")
Console.WriteLine("A VALUE BETWEEN THE FIRST TWO.")
Console.WriteLine("IF YOU DO NOT WANT TO BET, INPUT A 0")
Do ' This loop continues as long as the player wants to keep playing
Do ' This loop continues as long as the player has money to play
Console.WriteLine("")
Console.WriteLine($"YOU NOW HAVE {currentBalance} DOLLARS.")
Console.WriteLine("")
Console.WriteLine("HERE ARE YOUR NEXT TWO CARDS:")
' We need to ensure that card B is a higher value for our later comparison,
' so we will loop until we have two cards that meet this criteria
Do
cardA = rnd.Next(2, 14)
cardB = rnd.Next(2, 14)
Loop While cardA > cardB
' We use a function to display the text value of the numeric card value
' because we do this 3 times and a function reduces repetition of code
Console.WriteLine(DisplayCard(cardA))
Console.WriteLine(DisplayCard(cardB))
Do ' This loop continues until the player provides a valid wager value
Console.WriteLine("")
Console.WriteLine("WHAT IS YOUR BET")
currentWager = 0
input = Console.ReadLine
' Any input from the console is a string, but we require a number.
' Test the input to make sure it is a numeric value.
If Integer.TryParse(input, currentWager) Then
' Test to ensure the player has not wagered more than their balance
If currentWager > currentBalance Then
Console.WriteLine("SORRY, MY FRIEND, BUT YOU BET TOO MUCH.")
Console.WriteLine($"YOU HAVE ONLY {currentBalance} DOLLARS TO BET.")
Else
' The player has provided a numeric value that is less/equal to their balance,
' exit the loop and continue play
Exit Do
End If ' check player balance
End If ' check numeric input
Loop ' wager loop
' If the player is wagering, draw the third card, otherwise, mock them.
If currentWager > 0 Then
cardC = rnd.Next(2, 14)
Console.WriteLine(DisplayCard(cardC))
' The effort we made to have two cards in numeric order earlier makes this check easier,
' otherwise we would have to have a second check in the opposite direction
If cardC < cardA OrElse cardC >= cardB Then
Console.WriteLine("SORRY, YOU LOSE")
currentBalance -= currentWager ' Shorthand code to decrement a number (currentBalance=currentBalance - currentWager)
Else
Console.WriteLine("YOU WIN!!!")
currentBalance += currentWager ' Shorthand code to increment a number (currentBalance=currentBalance + currentWager)
End If
Else
Console.WriteLine("CHICKEN!!")
Console.WriteLine("")
End If
Loop While currentBalance > 0 ' loop as long as the player has money
' At this point, the player has no money (currentBalance=0). Inform them of such.
Console.WriteLine("")
Console.WriteLine("SORRY, FRIEND, BUT YOU BLEW YOUR WAD.")
Console.WriteLine("")
Console.WriteLine("")
' We will loop to ensure the player provides some answer.
Do
Console.WriteLine("TRY AGAIN (YES OR NO)")
Console.WriteLine("")
input = Console.ReadLine
Loop While String.IsNullOrWhiteSpace(input)
' We will assume that the player wants to play again only if they answer yes.
' (yeah and ya are valid as well, because we only check the first letter)
If input.Substring(0, 1).Equals("y", StringComparison.CurrentCultureIgnoreCase) Then ' This allows upper and lower case to be entered.
currentBalance = 100 ' Reset the players balance before restarting
Else
' Exit the outer loop which will end the game.
Exit Do
End If
Loop ' The full game loop
Console.WriteLine("O.K., HOPE YOU HAD FUN!")
End Sub
' This function is called for each of the 3 cards used in the game.
' The input and the output are both consistent, making it a good candidate for a function.
Private Function DisplayCard(value As Integer) As String
' We check the value of the input and run a block of code for whichever
' evaluation matches
Select Case value
Case 2 To 10 ' Case statements can be ranges of values, also multiple values (Case 2,3,4,5,6,7,8,9,10)
Return value.ToString
Case 11
Return "JACK"
Case 12
Return "QUEEN"
Case 13
Return "KING"
Case 14
Return "ACE"
End Select
' Although we have full knowledge of the program and never plan to send an invalid
' card value, it's important to provide a message for the next developer who won't
Throw New ArgumentOutOfRangeException("Card value must be between 2 and 14")
End Function
End Module
+21
View File
@@ -0,0 +1,21 @@
Imports System
''' <summary>
''' This is a modern adapation of Acey Ducey from BASIC Computer Games.
'''
''' The structural changes primarily consist of replacing the many GOTOs with
''' Do/Loop constructs to force the continual execution of the program.
'''
''' Some modern improvements were added, primarily the inclusion of a multiple
''' subroutines and functions, which eliminates repeated logic and reduces
''' then need for nested loops.
'''
''' The archaic RND function is greatly simplified with the .NET Framework's Random class.
'''
''' Elementary comments are provided for non-programmers or novices.
''' </summary>
Module Program
Sub Main()
Call New AceyDucey().Play()
End Sub
End Module
+22
View File
@@ -0,0 +1,22 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Amazing", "Amazing.vbproj", "{FB9DF301-CB34-4C9A-8823-F034303F5DA3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{FB9DF301-CB34-4C9A-8823-F034303F5DA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FB9DF301-CB34-4C9A-8823-F034303F5DA3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FB9DF301-CB34-4C9A-8823-F034303F5DA3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FB9DF301-CB34-4C9A-8823-F034303F5DA3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
+8
View File
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<RootNamespace>Amazing</RootNamespace>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>16.9</LangVersion>
</PropertyGroup>
</Project>
+295
View File
@@ -0,0 +1,295 @@
Imports System
Module Program
Enum Directions
SolveAndReset = 0
Left = 1
Up = 2
Right = 3
Down = 4
End Enum
'Program State
Dim Width As Integer = 0, Height As Integer = 0, Q As Integer = 0, CellsVisited As Integer = 2, curCol As Integer, curRow As Integer = 1
Dim SolutionCompleted As Boolean = False
Dim CellVisitHistory(,) As Integer
Dim CellState(,) As Integer
Dim rnd As New Random()
Public ReadOnly Property BlockedLeft As Boolean
Get
Return curCol - 1 = 0 OrElse CellVisitHistory(curCol - 1, curRow) <> 0
End Get
End Property
Public ReadOnly Property BlockedAbove As Boolean
Get
Return curRow - 1 = 0 OrElse CellVisitHistory(curCol, curRow - 1) <> 0
End Get
End Property
Public ReadOnly Property BlockedRight As Boolean
Get
Return curCol = Width OrElse CellVisitHistory(curCol + 1, curRow) <> 0
End Get
End Property
'Note: "BlockedBelow" does NOT include checking if we have a solution!
Public ReadOnly Property BlockedBelow As Boolean
Get
Return curRow = Height OrElse CellVisitHistory(curCol, curRow + 1) <> 0
End Get
End Property
Public ReadOnly Property OnBottomRow As Boolean
Get
Return curRow.Equals(Height)
End Get
End Property
Sub Main(args As String())
Const header As String =
" AMAZING PROGRAM
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
"
Console.WriteLine(header)
While Width <= 1 OrElse Height <= 1
Console.Write("WHAT ARE YOUR WIDTH AND LENGTH? ")
'We no longer have the old convenient INPUT command, so need to parse out the inputs
Dim parts = Console.ReadLine().Split(","c).Select(Function(s) Convert.ToInt32(s.Trim())).ToList()
Width = parts(0)
Height = parts(1)
If Width <= 1 OrElse Height <= 1 Then Console.WriteLine($"MEANINGLESS DIMENSIONS. TRY AGAIN.{vbCrLf}")
End While
ReDim CellVisitHistory(Width, Height), CellState(Width, Height)
Console.WriteLine("
")
curCol = rnd.Next(1, Width + 1) 'Starting X position
CellVisitHistory(curCol, 1) = 1
Dim startXPos As Integer = curCol 'we need to know this at the end to print opening line
Dim keepGoing As Boolean = True
While keepGoing
If BlockedLeft Then
keepGoing = ChoosePath_BlockedToTheLeft()
ElseIf BlockedAbove Then
keepGoing = ChoosePath_BlockedAbove()
ElseIf BlockedRight Then
keepGoing = ChoosePath_BlockedToTheRight()
Else
keepGoing = SelectRandomDirection(Directions.Left, Directions.Up, Directions.Right) 'Go anywhere but down
End If
End While
PrintFinalResults(startXPos)
End Sub
Public Sub ResetCurrentPosition()
Do
If curCol <> Width Then 'not at the right edge
curCol += 1
ElseIf curRow <> Height Then 'not at the bottom
curCol = 1
curRow += 1
Else
curCol = 1
curRow = 1
End If
Loop While CellVisitHistory(curCol, curRow) = 0
End Sub
Dim methods() As Func(Of Boolean) = {AddressOf MarkSolvedAndResetPosition, AddressOf GoLeft, AddressOf GoUp, AddressOf GoRight, AddressOf GoDown}
Public Function SelectRandomDirection(ParamArray possibles() As Directions) As Boolean
Dim x As Integer = rnd.Next(0, possibles.Length)
Return methods(possibles(x))()
End Function
Public Function ChoosePath_BlockedToTheLeft() As Boolean
If BlockedAbove Then
If BlockedRight Then
If curRow <> Height Then
If CellVisitHistory(curCol, curRow + 1) <> 0 Then ' Can't go down, but not at the edge...blocked. Reset and try again
ResetCurrentPosition()
Return True
Else
Return GoDown()
End If
ElseIf SolutionCompleted Then 'Can't go Down (there's already another solution)
ResetCurrentPosition()
Return True
Else 'Can't go LEFT, UP, RIGHT, or DOWN, but we're on the bottom and there's no solution yet
Return MarkSolvedAndResetPosition()
End If
ElseIf BlockedBelow Then
Return GoRight()
ElseIf Not OnBottomRow Then
Return SelectRandomDirection(Directions.Right, Directions.Down)
ElseIf SolutionCompleted Then 'Can only go right, and we're at the bottom
Return GoRight()
Else 'Can only go right, we're at the bottom, and there's not a solution yet
Return SelectRandomDirection(Directions.Right, Directions.SolveAndReset)
End If
'== Definitely can go Up ==
ElseIf BlockedRight Then
If BlockedBelow Then
Return GoUp()
ElseIf Not OnBottomRow Then
Return SelectRandomDirection(Directions.Up, Directions.Down)
ElseIf SolutionCompleted Then 'We're on the bottom row, can only go up
Return GoUp()
Else 'We're on the bottom row, can only go up, but there's no solution
Return SelectRandomDirection(Directions.Up, Directions.SolveAndReset)
End If
'== Definitely can go Up and Right ==
ElseIf BlockedBelow Then
Return SelectRandomDirection(Directions.Up, Directions.Right)
ElseIf Not OnBottomRow Then
Return SelectRandomDirection(Directions.Up, Directions.Right, Directions.Down)
ElseIf SolutionCompleted Then 'at the bottom, but already have a solution
Return SelectRandomDirection(Directions.Up, Directions.Right)
Else
Return SelectRandomDirection(Directions.Up, Directions.Right, Directions.SolveAndReset)
End If
End Function
Public Function ChoosePath_BlockedAbove() As Boolean
'No need to check the left side, only called from the "keepGoing" loop where LEFT is already cleared
If BlockedRight Then
If BlockedBelow Then
Return GoLeft()
ElseIf Not OnBottomRow Then
Return SelectRandomDirection(Directions.Left, Directions.Down)
ElseIf SolutionCompleted Then 'Can't go down because there's already a solution
Return GoLeft()
Else 'At the bottom, no solution yet...
Return SelectRandomDirection(Directions.Left, Directions.SolveAndReset)
End If
ElseIf BlockedBelow Then
Return SelectRandomDirection(Directions.Left, Directions.Right)
ElseIf Not OnBottomRow Then
Return SelectRandomDirection(Directions.Left, Directions.Right, Directions.Down)
ElseIf SolutionCompleted Then
Return SelectRandomDirection(Directions.Left, Directions.Right)
Else
Return SelectRandomDirection(Directions.Left, Directions.Right, Directions.SolveAndReset)
End If
End Function
Public Function ChoosePath_BlockedToTheRight() As Boolean
'No need to check Left or Up, only called from the "keepGoing" loop where LEFT and UP are already cleared
If BlockedRight Then 'Can't go Right -- why? we knew this when calling the function
If BlockedBelow Then
Return SelectRandomDirection(Directions.Left, Directions.Up)
ElseIf Not OnBottomRow Then
Return SelectRandomDirection(Directions.Left, Directions.Up, Directions.Down)
ElseIf SolutionCompleted Then
Return SelectRandomDirection(Directions.Left, Directions.Up)
Else
Return SelectRandomDirection(Directions.Left, Directions.Up, Directions.SolveAndReset)
End If
Else 'Should never get here
Return SelectRandomDirection(Directions.Left, Directions.Up, Directions.Right) 'Go Left, Up, or Right (but path is blocked?)
End If
End Function
Public Sub PrintFinalResults(startPos As Integer)
For i As Integer = 0 To Width - 1
If i = startPos Then Console.Write(". ") Else Console.Write(".--")
Next
Console.WriteLine(".")
If Not SolutionCompleted Then 'Pick a random exit
Dim X As Integer = rnd.Next(1, Width + 1)
If CellState(X, Height) = 0 Then
CellState(X, Height) = 1
Else
CellState(X, Height) = 3
End If
End If
For j As Integer = 1 To Height
Console.Write("I")
For i As Integer = 1 To Width
If CellState(i, j) < 2 Then
Console.Write(" I")
Else
Console.Write(" ")
End If
Next
Console.WriteLine()
For i As Integer = 1 To Width
If CellState(i, j) = 0 OrElse CellState(i, j) = 2 Then
Console.Write(":--")
Else
Console.Write(": ")
End If
Next
Console.WriteLine(".")
Next
End Sub
Public Function GoLeft() As Boolean
curCol -= 1
CellVisitHistory(curCol, curRow) = CellsVisited
CellsVisited += 1
CellState(curCol, curRow) = 2
If CellsVisited > Width * Height Then Return False
Q = 0
Return True
End Function
Public Function GoUp() As Boolean
curRow -= 1
CellVisitHistory(curCol, curRow) = CellsVisited
CellsVisited += 1
CellState(curCol, curRow) = 1
If CellsVisited > Width * Height Then Return False
Q = 0
Return True
End Function
Public Function GoRight() As Boolean
CellVisitHistory(curCol + 1, curRow) = CellsVisited
CellsVisited += 1
If CellState(curCol, curRow) = 0 Then CellState(curCol, curRow) = 2 Else CellState(curCol, curRow) = 3
curCol += 1
If CellsVisited > Width * Height Then Return False
Return ChoosePath_BlockedToTheLeft()
End Function
Public Function GoDown() As Boolean
If Q = 1 Then Return MarkSolvedAndResetPosition()
CellVisitHistory(curCol, curRow + 1) = CellsVisited
CellsVisited += 1
If CellState(curCol, curRow) = 0 Then CellState(curCol, curRow) = 1 Else CellState(curCol, curRow) = 3
curRow += 1
If CellsVisited > Width * Height Then Return False
Return True
End Function
Public Function MarkSolvedAndResetPosition() As Boolean
' AlWAYS returns true
SolutionCompleted = True
Q = 1
If CellState(curCol, curRow) = 0 Then
CellState(curCol, curRow) = 1
curCol = 1
curRow = 1
If CellVisitHistory(curCol, curRow) = 0 Then ResetCurrentPosition()
Else
CellState(curCol, curRow) = 3
ResetCurrentPosition()
End If
Return True
End Function
End Module
-160
View File
@@ -1,160 +0,0 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Scanner;
/**
* ANIMAL
* <p>
* Converted from BASIC to Java by Aldrin Misquitta (@aldrinm)
*/
public class Animal {
public static void main(String[] args) {
printIntro();
Scanner scan = new Scanner(System.in);
List<Question> questions = new ArrayList<>();
questions.add(new Question("DOES IT SWIM", "FISH", "BIRD"));
boolean stopGame = false;
while (!stopGame) {
String choice = readMainChoice(scan);
switch (choice) {
case "LIST":
printKnownAnimals(questions);
break;
case "Q":
case "QUIT":
stopGame = true;
break;
default:
if (choice.toUpperCase(Locale.ROOT).startsWith("Y")) {
int k = 0;
boolean correctGuess = false;
while (questions.size() > k && !correctGuess) {
Question question = questions.get(k);
correctGuess = askQuestion(question, scan);
if (correctGuess) {
System.out.println("WHY NOT TRY ANOTHER ANIMAL?");
} else {
k++;
}
}
if (!correctGuess) {
askForInformationAndSave(scan, questions);
}
}
}
}
}
private static void askForInformationAndSave(Scanner scan, List<Question> questions) {
//Failed to get it right and ran out of questions
//Let's ask the user for the new information
System.out.print("THE ANIMAL YOU WERE THINKING OF WAS A ");
String animal = scan.nextLine();
System.out.printf("PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A %s FROM A %s ", animal, questions.get(
questions.size() - 1).falseAnswer);
String newQuestion = scan.nextLine();
System.out.printf("FOR A %s THE ANSWER WOULD BE ", animal);
boolean newAnswer = readYesOrNo(scan);
//Add it to our list
addNewAnimal(questions, animal, newQuestion, newAnswer);
}
private static void addNewAnimal(List<Question> questions, String animal, String newQuestion, boolean newAnswer) {
Question lastQuestion = questions.get(questions.size() - 1);
String lastAnimal = lastQuestion.falseAnswer;
lastQuestion.falseAnswer = null; //remove the false option to indicate that there is a next question
Question newOption;
if (newAnswer) {
newOption = new Question(newQuestion, animal, lastAnimal);
} else {
newOption = new Question(newQuestion, lastAnimal, animal);
}
questions.add(newOption);
}
private static boolean askQuestion(Question question, Scanner scanner) {
System.out.printf("%s ? ", question.question);
boolean chosenAnswer = readYesOrNo(scanner);
if (chosenAnswer) {
if (question.trueAnswer != null) {
System.out.printf("IS IT A %s ? ", question.trueAnswer);
return readYesOrNo(scanner);
}
//else go to the next question
} else {
if (question.falseAnswer != null) {
System.out.printf("IS IT A %s ? ", question.falseAnswer);
return readYesOrNo(scanner);
}
//else go to the next question
}
return false;
}
private static boolean readYesOrNo(Scanner scanner) {
boolean validAnswer = false;
Boolean choseAnswer = null;
while (!validAnswer) {
String answer = scanner.nextLine();
if (answer.toUpperCase(Locale.ROOT).startsWith("Y")) {
validAnswer = true;
choseAnswer = true;
} else if (answer.toUpperCase(Locale.ROOT).startsWith("N")) {
validAnswer = true;
choseAnswer = false;
}
}
return choseAnswer;
}
private static void printKnownAnimals(List<Question> questions) {
System.out.println("\nANIMALS I ALREADY KNOW ARE:");
List<String> animals = new ArrayList<>();
questions.forEach(q -> {
if (q.trueAnswer != null) {
animals.add(q.trueAnswer);
}
if (q.falseAnswer != null) {
animals.add(q.falseAnswer);
}
});
System.out.println(String.join("\t\t", animals));
}
private static String readMainChoice(Scanner scan) {
System.out.print("ARE YOU THINKING OF AN ANIMAL ? ");
return scan.nextLine();
}
private static void printIntro() {
System.out.println(" ANIMAL");
System.out.println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
System.out.println("\n\n");
System.out.println("PLAY 'GUESS THE ANIMAL'");
System.out.println("\n");
System.out.println("THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT.");
}
public static class Question {
String question;
String trueAnswer;
String falseAnswer;
public Question(String question, String trueAnswer, String falseAnswer) {
this.question = question;
this.trueAnswer = trueAnswer;
this.falseAnswer = falseAnswer;
}
}
}
+247
View File
@@ -0,0 +1,247 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Scanner;
import java.util.stream.Collectors;
/**
* ANIMAL
* <p>
* Converted from BASIC to Java by Aldrin Misquitta (@aldrinm)
* The original BASIC program uses an array to maintain the questions and answers and to decide which question to
* ask next. Updated this Java implementation to use a tree instead of the earlier faulty one based on a list (thanks @patimen).
*
* Bonus option: TREE --> prints the game decision data as a tree to visualize/debug the state of the game
*/
public class Animal {
public static void main(String[] args) {
printIntro();
Scanner scan = new Scanner(System.in);
Node root = new QuestionNode("DOES IT SWIM",
new AnimalNode("FISH"), new AnimalNode("BIRD"));
boolean stopGame = false;
while (!stopGame) {
String choice = readMainChoice(scan);
switch (choice) {
case "TREE":
printTree(root);
break;
case "LIST":
printKnownAnimals(root);
break;
case "Q":
case "QUIT":
stopGame = true;
break;
default:
if (choice.toUpperCase(Locale.ROOT).startsWith("Y")) {
Node current = root; //where we are in the question tree
Node previous; //keep track of parent of current in order to place new questions later on.
while (current instanceof QuestionNode) {
var currentQuestion = (QuestionNode) current;
var reply = askQuestionAndGetReply(currentQuestion, scan);
previous = current;
current = reply ? currentQuestion.getTrueAnswer() : currentQuestion.getFalseAnswer();
if (current instanceof AnimalNode) {
//We have reached a animal node, so offer it as the guess
var currentAnimal = (AnimalNode) current;
System.out.printf("IS IT A %s ? ", currentAnimal.getAnimal());
var animalGuessResponse = readYesOrNo(scan);
if (animalGuessResponse) {
//we guessed right! end this round
System.out.println("WHY NOT TRY ANOTHER ANIMAL?");
} else {
//we guessed wrong :(, ask for feedback
//cast previous to QuestionNode since we know at this point that it is not a leaf node
askForInformationAndSave(scan, currentAnimal, (QuestionNode) previous, reply);
}
}
}
}
}
}
}
/**
* Prompt for information about the animal we got wrong
* @param current The animal that we guessed wrong
* @param previous The root of current
* @param previousToCurrentDecisionChoice Whether it was a Y or N answer that got us here. true = Y, false = N
*/
private static void askForInformationAndSave(Scanner scan, AnimalNode current, QuestionNode previous, boolean previousToCurrentDecisionChoice) {
//Failed to get it right and ran out of questions
//Let's ask the user for the new information
System.out.print("THE ANIMAL YOU WERE THINKING OF WAS A ? ");
String animal = scan.nextLine();
System.out.printf("PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A %s FROM A %s ? ", animal, current.getAnimal());
String newQuestion = scan.nextLine();
System.out.printf("FOR A %s THE ANSWER WOULD BE ? ", animal);
boolean newAnswer = readYesOrNo(scan);
//Add it to our question store
addNewAnimal(current, previous, animal, newQuestion, newAnswer, previousToCurrentDecisionChoice);
}
private static void addNewAnimal(Node current,
QuestionNode previous,
String animal,
String newQuestion,
boolean newAnswer,
boolean previousToCurrentDecisionChoice) {
var animalNode = new AnimalNode(animal);
var questionNode = new QuestionNode(newQuestion,
newAnswer ? animalNode : current,
!newAnswer ? animalNode : current);
if (previous != null) {
if (previousToCurrentDecisionChoice) {
previous.setTrueAnswer(questionNode);
} else {
previous.setFalseAnswer(questionNode);
}
}
}
private static boolean askQuestionAndGetReply(QuestionNode questionNode, Scanner scanner) {
System.out.printf("%s ? ", questionNode.question);
return readYesOrNo(scanner);
}
private static boolean readYesOrNo(Scanner scanner) {
boolean validAnswer = false;
Boolean choseAnswer = null;
while (!validAnswer) {
String answer = scanner.nextLine();
if (answer.toUpperCase(Locale.ROOT).startsWith("Y")) {
validAnswer = true;
choseAnswer = true;
} else if (answer.toUpperCase(Locale.ROOT).startsWith("N")) {
validAnswer = true;
choseAnswer = false;
}
}
return choseAnswer;
}
private static void printKnownAnimals(Node root) {
System.out.println("\nANIMALS I ALREADY KNOW ARE:");
List<AnimalNode> leafNodes = collectLeafNodes(root);
String allAnimalsString = leafNodes.stream().map(AnimalNode::getAnimal).collect(Collectors.joining("\t\t"));
System.out.println(allAnimalsString);
}
//Traverse the tree and collect all the leaf nodes, which basically have all the animals.
private static List<AnimalNode> collectLeafNodes(Node root) {
List<AnimalNode> collectedNodes = new ArrayList<>();
if (root instanceof AnimalNode) {
collectedNodes.add((AnimalNode) root);
} else {
var q = (QuestionNode) root;
collectedNodes.addAll(collectLeafNodes(q.getTrueAnswer()));
collectedNodes.addAll(collectLeafNodes(q.getFalseAnswer()));
}
return collectedNodes;
}
private static String readMainChoice(Scanner scan) {
System.out.print("ARE YOU THINKING OF AN ANIMAL ? ");
return scan.nextLine();
}
private static void printIntro() {
System.out.println(" ANIMAL");
System.out.println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
System.out.println("\n\n");
System.out.println("PLAY 'GUESS THE ANIMAL'");
System.out.println("\n");
System.out.println("THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT.");
}
//Based on https://stackoverflow.com/a/8948691/74057
private static void printTree(Node root) {
StringBuilder buffer = new StringBuilder(50);
print(root, buffer, "", "");
System.out.println(buffer);
}
private static void print(Node root, StringBuilder buffer, String prefix, String childrenPrefix) {
buffer.append(prefix);
buffer.append(root.toString());
buffer.append('\n');
if (root instanceof QuestionNode) {
var questionNode = (QuestionNode) root;
print(questionNode.getTrueAnswer(), buffer, childrenPrefix + "├─Y─ ", childrenPrefix + "");
print(questionNode.getFalseAnswer(), buffer, childrenPrefix + "└─N─ ", childrenPrefix + " ");
}
}
/**
* Base interface for all nodes in our question tree
*/
private interface Node {
}
private static class QuestionNode implements Node {
private final String question;
private Node trueAnswer;
private Node falseAnswer;
public QuestionNode(String question, Node trueAnswer, Node falseAnswer) {
this.question = question;
this.trueAnswer = trueAnswer;
this.falseAnswer = falseAnswer;
}
public String getQuestion() {
return question;
}
public Node getTrueAnswer() {
return trueAnswer;
}
public void setTrueAnswer(Node trueAnswer) {
this.trueAnswer = trueAnswer;
}
public Node getFalseAnswer() {
return falseAnswer;
}
public void setFalseAnswer(Node falseAnswer) {
this.falseAnswer = falseAnswer;
}
@Override
public String toString() {
return "Question{'" + question + "'}";
}
}
private static class AnimalNode implements Node {
private final String animal;
public AnimalNode(String animal) {
this.animal = animal;
}
public String getAnimal() {
return animal;
}
@Override
public String toString() {
return "Animal{'" + animal + "'}";
}
}
}
+55
View File
@@ -0,0 +1,55 @@
import com.pcholt.console.testutils.ConsoleTest
import org.junit.Test
class AnimalJavaTest : ConsoleTest() {
@Test
fun `given a standard setup, find the fish`() {
assertConversation(
"""
$title
ARE YOU THINKING OF AN ANIMAL ? {YES}
DOES IT SWIM ? {YES}
IS IT A FISH ? {YES}
WHY NOT TRY ANOTHER ANIMAL?
ARE YOU THINKING OF AN ANIMAL ? {QUIT}
"""
) {
Animal.main(emptyArray())
}
}
@Test
fun `given a standard setup, create a cow, and verify`() {
assertConversation(
"""
$title
ARE YOU THINKING OF AN ANIMAL ? {YES}
DOES IT SWIM ? {NO}
IS IT A BIRD ? {NO}
THE ANIMAL YOU WERE THINKING OF WAS A ? {COW}
PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A
COW FROM A BIRD
? {DOES IT EAT GRASS}
FOR A COW THE ANSWER WOULD BE ? {YES}
ARE YOU THINKING OF AN ANIMAL ? {YES}
DOES IT SWIM ? {NO}
DOES IT EAT GRASS ? {YES}
IS IT A COW ? {YES}
WHY NOT TRY ANOTHER ANIMAL?
ARE YOU THINKING OF AN ANIMAL ? {QUIT}
"""
) {
Animal.main(emptyArray())
}
}
private val title = """
ANIMAL
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
PLAY 'GUESS THE ANIMAL'
THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT.
"""
}
+55
View File
@@ -0,0 +1,55 @@
import com.pcholt.console.testutils.ConsoleTest
import org.junit.Test
class AnimalKtTest : ConsoleTest() {
@Test
fun `given a standard setup, find the fish`() {
assertConversation(
"""
$title
ARE YOU THINKING OF AN ANIMAL? {YES}
DOES IT SWIM? {YES}
IS IT A FISH? {YES}
WHY NOT TRY ANOTHER ANIMAL?
ARE YOU THINKING OF AN ANIMAL? {QUIT}
"""
) {
main()
}
}
@Test
fun `given a standard setup, create a cow, and verify`() {
assertConversation(
"""
$title
ARE YOU THINKING OF AN ANIMAL? {YES}
DOES IT SWIM? {NO}
IS IT A BIRD? {NO}
THE ANIMAL YOU WERE THINKING OF WAS A? {COW}
PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A
COW FROM A BIRD
? {DOES IT EAT GRASS}
FOR A COW THE ANSWER WOULD BE? {YES}
ARE YOU THINKING OF AN ANIMAL? {YES}
DOES IT SWIM? {NO}
DOES IT EAT GRASS? {YES}
IS IT A COW? {YES}
WHY NOT TRY ANOTHER ANIMAL?
ARE YOU THINKING OF AN ANIMAL? {QUIT}
"""
) {
main()
}
}
private val title = """
ANIMAL
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
PLAY 'GUESS THE ANIMAL'
THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT.
"""
}
@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>Animal.Tests</RootNamespace>
<TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable>
<OptionStrict>On</OptionStrict>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Animal\Animal.vbproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,7 @@
''' <summary>
''' <para>Indicates that there are no more inputs in the MockConsole.</para>
''' We need this while testing, because otherwise the game loop will continue forever, waiting for a nonexistent input.
''' </summary>
Public Class EndOfInputsException
Inherits Exception
End Class
@@ -0,0 +1,60 @@
Imports System.IO
Public Class MockConsole
Inherits ConsoleAdapterBase
Private inputs As Queue(Of String)
Public ReadOnly Lines As New List(Of (line As String, centered As Boolean)) From {
("", False)
}
' TODO it's possible to clear all the lines, and we'd have to check once again in WriteString and WriteCenteredLine if there are any lines
Sub New(Inputs As IEnumerable(Of String))
Me.inputs = New Queue(Of String)(Inputs)
End Sub
Private Sub CheckLinesInitialized()
If Lines.Count = 0 Then Lines.Add(("", False))
End Sub
Private Sub WriteString(s As String, Optional centered As Boolean = False)
If s Is Nothing Then Return
CheckLinesInitialized()
s.Split(Environment.NewLine).ForEach(Sub(line, index)
If index = 0 Then
Dim currentLast = Lines(Lines.Count - 1)
' centered should never come from the current last line
' if WriteCenteredLine is called, it immediately creates a new line
Lines(Lines.Count - 1) = (currentLast.line + line, centered)
Else
Lines.Add((line, centered))
End If
End Sub)
End Sub
Public Overrides Sub Write(value As Object)
WriteString(value?.ToString)
End Sub
Public Overrides Sub WriteLine()
Lines.Add(("", False))
End Sub
Public Overrides Sub WriteCenteredLines(value As Object)
If Lines.Count = 0 Then Lines.Add(("", False))
Dim currentLast = Lines(Lines.Count - 1).line
If currentLast.Length > 0 Then Lines.Add(("", False))
WriteString(value?.ToString, True)
WriteLine()
End Sub
Public Overrides Function ReadLine() As String
' Indicates the end of a test run, for programs which loop endlessly
If inputs.Count = 0 Then Throw New EndOfInputsException
Dim nextInput = inputs.Dequeue.Trim
WriteLine(nextInput)
Return nextInput
End Function
End Class
@@ -0,0 +1,118 @@
Imports Xunit
Imports Animal
Imports System.IO
Public Class TestContainer
Private Shared Function ResponseVariantExpander(src As IEnumerable(Of String)) As TheoryData(Of String)
Dim theoryData = New TheoryData(Of String)
src.
SelectMany(Function(x) {x, x.Substring(0, 1)}).
SelectMany(Function(x) {
x,
x.ToUpperInvariant,
x.ToLowerInvariant,
x.ToTitleCase,
x.ToReverseCase
}).
Distinct.
ForEach(Sub(x) theoryData.Add(x))
Return theoryData
End Function
Private Shared YesVariantsThepryData As TheoryData(Of String) = ResponseVariantExpander({"yes", "true", "1"})
Private Shared Function YesVariants() As TheoryData(Of String)
Return YesVariantsThepryData
End Function
Private Shared NoVariantsThepryData As TheoryData(Of String) = ResponseVariantExpander({"no", "false", "0"})
Private Shared Function NoVariants() As TheoryData(Of String)
Return NoVariantsThepryData
End Function
''' <summary>Test LIST variants</summary>
<Theory>
<InlineData("LIST")>
<InlineData("list")>
<InlineData("List")>
<InlineData("lIST")>
Sub List(listResponse As String)
Dim console As New MockConsole({listResponse})
Dim game As New Game(console)
Assert.Throws(Of EndOfInputsException)(Sub() game.BeginLoop())
Assert.Equal(
{
"ANIMALS I ALREADY KNOW ARE:",
"FISH BIRD "
},
console.Lines.Slice(-4, -2).Select(Function(x) x.line)
)
End Sub
'' <summary>Test YES variants</summary>
<Theory>
<MemberData(NameOf(YesVariants))>
Sub YesVariant(yesVariant As String)
Dim console As New MockConsole({yesVariant})
Dim game As New Game(console)
Assert.Throws(Of EndOfInputsException)(Sub() game.BeginLoop())
Assert.Equal(
{
$"ARE YOU THINKING OF AN ANIMAL? {yesVariant}",
"DOES IT SWIM? "
},
console.Lines.Slice(-2, 0).Select(Function(x) x.line)
)
End Sub
'' <summary>Test NO variants</summary>
<Theory>
<MemberData(NameOf(NoVariants))>
Sub NoVariant(noVariant As String)
Dim console As New MockConsole({"y", noVariant})
Dim game As New Game(console)
Assert.Throws(Of EndOfInputsException)(Sub() game.BeginLoop())
Assert.Equal(
{
$"DOES IT SWIM? {noVariant}",
"IS IT A BIRD? "
},
console.Lines.Slice(-2, 0).Select(Function(x) x.line)
)
End Sub
''' <summary>Test adding a new animal and using the new animal in the game</summary>
<Fact>
Sub TestAddedAnimal()
Dim console As New MockConsole({
"y",
"y",
"n",
"whale",
"is it a mammal?",
"y",
"y",
"y",
"y",
"y"
})
Dim game As New Game(console)
Assert.Throws(Of EndOfInputsException)(Sub() game.BeginLoop())
Assert.Equal(
{
"ARE YOU THINKING OF AN ANIMAL? y",
"DOES IT SWIM? y",
"IS IT A FISH? n",
"THE ANIMAL YOU WERE THINKING OF WAS A ? whale",
"PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A",
"WHALE FROM A FISH",
"is it a mammal?",
"FOR A WHALE THE ANSWER WOULD BE? y",
"ARE YOU THINKING OF AN ANIMAL? y",
"DOES IT SWIM? y",
"IS IT A MAMMAL? y",
"IS IT A WHALE? y",
"WHY NOT TRY ANOTHER ANIMAL?",
"ARE YOU THINKING OF AN ANIMAL? "
},
console.Lines.Slice(9, 100).Select(Function(x) x.line)
)
End Sub
End Class
+31
View File
@@ -0,0 +1,31 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.32112.339
MinimumVisualStudioVersion = 10.0.40219.1
Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "Animal", "Animal\Animal.vbproj", "{5517E4CE-BCF9-4D1F-9A17-B620C1B96B0D}"
EndProject
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Animal.Tests", "Animal.Tests\Animal.Tests.vbproj", "{3986C6A2-77D4-4F00-B3CF-F5736C623B1E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5517E4CE-BCF9-4D1F-9A17-B620C1B96B0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5517E4CE-BCF9-4D1F-9A17-B620C1B96B0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5517E4CE-BCF9-4D1F-9A17-B620C1B96B0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5517E4CE-BCF9-4D1F-9A17-B620C1B96B0D}.Release|Any CPU.Build.0 = Release|Any CPU
{3986C6A2-77D4-4F00-B3CF-F5736C623B1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3986C6A2-77D4-4F00-B3CF-F5736C623B1E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3986C6A2-77D4-4F00-B3CF-F5736C623B1E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3986C6A2-77D4-4F00-B3CF-F5736C623B1E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {88469A47-E30C-4763-A325-074101D16608}
EndGlobalSection
EndGlobal
+9
View File
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<RootNamespace>Animal</RootNamespace>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>16.9</LangVersion>
<OptionStrict>On</OptionStrict>
</PropertyGroup>
</Project>
+29
View File
@@ -0,0 +1,29 @@
Public Class Branch
Public Property Text As String
Public ReadOnly Property IsEnd As Boolean
Get
Return Yes Is Nothing AndAlso No Is Nothing
End Get
End Property
Public Property Yes As Branch
Public Property No As Branch
' Allows walking all the descendants recursively
Public Iterator Function DescendantTexts() As IEnumerable(Of String)
If Yes IsNot Nothing Then
Yield Yes.Text
For Each childText In Yes.DescendantTexts
Yield childText
Next
End If
If No IsNot Nothing Then
Yield No.Text
For Each childText In No.DescendantTexts
Yield childText
Next
End If
End Function
End Class
+158
View File
@@ -0,0 +1,158 @@
Option Compare Text
Public Class Game
' This Dictionary holds the corresponding value for each of the variants of "YES" and "NO" we accept
' Note that the Dictionary is case-insensitive, meaning it maps "YES", "yes" and even "yEs" to True
Private Shared ReadOnly YesNoResponses As New Dictionary(Of String, Boolean)(StringComparer.InvariantCultureIgnoreCase) From {
{"yes", True},
{"y", True},
{"true", True},
{"t", True},
{"1", True},
{"no", False},
{"n", False},
{"false", False},
{"f", False},
{"0", False}
}
ReadOnly console As ConsoleAdapterBase
' The pre-initialized root branch
ReadOnly root As New Branch With {
.Text = "DOES IT SWIM?",
.Yes = New Branch With {.Text = "FISH"},
.No = New Branch With {.Text = "BIRD"}
}
''' <summary>Reduces a string or console input to True, False or Nothing. Case-insensitive.</summary>
''' <param name="s">Optional String to reduce via the same logic. If not passed in, will use console.ReadLine</param>
''' <returns>
''' Returns True for a "yes" response (yes, y, true, t, 1) and False for a "no" response (no, n, false, f, 0).<br/>
''' Returns Nothing if the response doesn't match any of these.
''' </returns>
Private Function GetYesNo(Optional s As String = Nothing) As Boolean?
s = If(s, console.ReadLine)
Dim ret As Boolean
If YesNoResponses.TryGetValue(s, ret) Then Return ret
Return Nothing
End Function
Sub New(console As ConsoleAdapterBase)
If console Is Nothing Then Throw New ArgumentNullException(NameOf(console))
Me.console = console
End Sub
Sub BeginLoop()
' Print the program heading
console.WriteCenteredLines(
"ANIMAL
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")
' Print the program description
console.Write(
"
PLAY 'GUESS THE ANIMAL'
THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT.
")
Do
console.Write("ARE YOU THINKING OF AN ANIMAL? ")
Dim response = console.ReadLine
If response = "list" Then
' List all the stored animals
console.Write(
"
ANIMALS I ALREADY KNOW ARE:
")
' We're using a ForEach extension method instead of the regular For Each loop to provide the index alongside the text
root.DescendantTexts.ForEach(Sub(text, index)
' We want to move to the next line after every four animals
' But for the first animal, where the index is 0, 0 Mod 4 will also return 0
' So we have to explicitly exclude the first animal
If index > 0 AndAlso index Mod 4 = 0 Then console.WriteLine()
console.Write($"{text.MaxLength(15),-15}")
End Sub)
console.Write(
"
")
Continue Do
End If
Dim ynResponse = GetYesNo(response)
If ynResponse Is Nothing OrElse Not ynResponse Then Continue Do
Dim currentBranch = root
Do While Not currentBranch.IsEnd
' Branches can either be questions, or end branches
' We have to walk the questions, prompting each time for "yes" or "no"
console.Write($"{currentBranch.Text} ")
Do
ynResponse = GetYesNo()
Loop While ynResponse Is Nothing
' Depending on the answer, we'll follow either the branch at "Yes" or "No"
currentBranch = If(
ynResponse,
currentBranch.Yes,
currentBranch.No
)
Loop
' Now we're at an end branch
console.Write($"IS IT A {currentBranch.Text}? ")
ynResponse = GetYesNo()
If ynResponse Then ' Only if ynResponse = True will we go into this If Then block
' The computer guessed the animal; we can go back to the beginning of the game
console.WriteLine("WHY NOT TRY ANOTHER ANIMAL?")
Continue Do
End If
' Get the new animal from the user
console.Write("THE ANIMAL YOU WERE THINKING OF WAS A ? ")
Dim newAnimal = console.ReadLine.ToUpperInvariant
' Get the question used to distinguish the new animal from the current end branch
console.Write(
$"PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A
{newAnimal} FROM A {currentBranch.Text}
")
Dim newQuestion = console.ReadLine.ToUpperInvariant
' Get the answer to that question, for the new animal
' for the old animal, the answer will be the opposite
console.Write($"FOR A {newAnimal} THE ANSWER WOULD BE? ")
Do
ynResponse = GetYesNo()
Loop While ynResponse Is Nothing
' Create the new end branch for the new animal
Dim newBranch = New Branch With {.Text = newAnimal}
' Copy over the current animal to another new end branch
Dim currentBranchCopy = New Branch With {.Text = currentBranch.Text}
' Make the current branch into the distinguishing question
currentBranch.Text = newQuestion
' Set the Yes and No branches of the current branch according to the answer
If ynResponse Then
currentBranch.Yes = newBranch
currentBranch.No = currentBranchCopy
Else
currentBranch.No = newBranch
currentBranch.Yes = currentBranchCopy
End If
' TODO how do we exit?
Loop
End Sub
End Class
+6
View File
@@ -0,0 +1,6 @@
Module Program
Sub Main()
Dim game As New Game(New ConsoleAdapter)
game.BeginLoop()
End Sub
End Module
@@ -0,0 +1,28 @@
Public Class ConsoleAdapter
Inherits ConsoleAdapterBase
Public Overrides Sub Write(value As Object)
Console.Write(value)
End Sub
Public Overrides Sub WriteLine()
Console.WriteLine()
End Sub
Public Overrides Sub WriteCenteredLines(value As Object)
If Console.CursorLeft <> 0 Then WriteLine()
Dim toWrite = If(value?.ToString, "")
For Each line In toWrite.Split(Environment.NewLine)
Write($"{Space((Console.WindowWidth - line.Length) \ 2)}{line}")
WriteLine()
Next
End Sub
Public Overrides Function ReadLine() As String
Dim response As String
Do
response = Console.ReadLine
Loop While response Is Nothing
Return response.Trim
End Function
End Class
@@ -0,0 +1,13 @@
Public MustInherit Class ConsoleAdapterBase
Public MustOverride Sub Write(value As Object)
Public MustOverride Sub WriteLine()
Public MustOverride Sub WriteCenteredLines(value As Object)
''' <summary>Implementations should always return a String without leading or trailing whitespace, never Nothng</summary>
Public MustOverride Function ReadLine() As String
Public Sub WriteLine(value As Object)
Write(value)
WriteLine()
End Sub
End Class
@@ -0,0 +1,50 @@
Imports System.Runtime.CompilerServices
Public Module Extensions
<Extension> Public Sub ForEach(Of T)(src As IEnumerable(Of T), action As Action(Of T))
For Each x In src
action(x)
Next
End Sub
<Extension> Public Sub ForEach(Of T)(src As IEnumerable(Of T), action As Action(Of T, Integer))
Dim index As Integer
For Each x In src
action(x, index)
index += 1
Next
End Sub
<Extension> Public Function MaxLength(s As String, value As Integer) As String
If s Is Nothing Then Return Nothing
Return s.Substring(0, Math.Min(s.Length, value))
End Function
<Extension> Public Function ForceEndsWith(s As String, toAppend As String) As String
If Not s.EndsWith(toAppend, StringComparison.OrdinalIgnoreCase) Then s += toAppend
Return s
End Function
<Extension> Public Function ToTitleCase(s As String) As String
If s Is Nothing Then Return Nothing
Return Char.ToUpperInvariant(s(0)) + s.Substring(1).ToUpperInvariant
End Function
' https://stackoverflow.com/a/3681580/111794
<Extension> Public Function ToReverseCase(s As String) As String
If s Is Nothing Then Return Nothing
Return New String(s.Select(Function(c) If(
Not Char.IsLetter(c),
c,
If(
Char.IsUpper(c), Char.ToLowerInvariant(c), Char.ToUpperInvariant(c)
)
)).ToArray)
End Function
' https://stackoverflow.com/a/58132204/111794
<Extension> Public Function Slice(Of T)(lst As IList(Of T), start As Integer, [end] As Integer) As T()
start = If(start >= 0, start, lst.Count + start)
[end] = If([end] > 0, [end], lst.Count + [end])
Return lst.Skip(start).Take([end] - start).ToArray
End Function
End Module
+6
View File
@@ -1,3 +1,9 @@
Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
This takes some inspiration from the [C# port of Animal](https://github.com/zspitz/basic-computer-games/tree/main/03_Animal/csharp).
The `Game` class takes a console abstraction (`ConsoleAdapterBase`), which could also be used for different UIs, such as WinForms or a web page.
This solution also has an xUnit tests project.
Responses can be entered in any capitalization, but animals and the distinguishing question will be converted to uppercase.
+25
View File
@@ -0,0 +1,25 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.32014.148
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Awari", "Awari.csproj", "{DD161F58-D90F-481A-8275-96E01D229A70}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DD161F58-D90F-481A-8275-96E01D229A70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DD161F58-D90F-481A-8275-96E01D229A70}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DD161F58-D90F-481A-8275-96E01D229A70}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DD161F58-D90F-481A-8275-96E01D229A70}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7F5C288A-A6C6-4AC0-96E3-6A3B482A0947}
EndGlobalSection
EndGlobal
+1 -1
View File
@@ -1,3 +1,3 @@
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
Conversion to [Ruby](https://www.ruby-lang.org/en/)
Conversion to [Ruby](https://www.ruby-lang.org/en/) by [Alex Scown](https://github.com/TheScown)
+311
View File
@@ -0,0 +1,311 @@
require 'strscan'
# Prints a number according to Vintage Basic's PRINT statement
# @param n The number to print
def print_number(n)
# PRINT adds padding after a number and before a positive number
print ' ' if n >= 0
print n.to_s
print ' '
end
# Mimic the INPUT statement using Vintage Basic as a reference
# @param prompt The prompt to show to the user
# @return An array of strings representing the inputted values
def input(prompt)
prompt_suffix = '? '
print "#{prompt}#{prompt_suffix}"
input = gets.chomp.strip
scanner = StringScanner.new(input)
input_values = []
until scanner.eos?
scanner.scan(/\s+/)
if scanner.check(/"/)
scanner.scan(/"/)
next_string = scanner.scan_until(/"/)
if next_string
# Remove the trailing close quote
next_string.chomp!('"')
else
# No close quote Vintage Basic crashes in this case
raise 'Unmatched quotes in input'
end
elsif scanner.exist?(/,/)
next_string = scanner.scan_until(/,/).chomp(',')
else
next_string = scanner.scan_until(/\s+|$/).rstrip
end
input_values << next_string
end
input_values << '' if input_values.empty?
input_values
end
class Game
def initialize(history, non_win_count)
@beans = Array.new(13, 3)
@beans[6] = 0
@beans[13] = 0
@turn_counter = 0
@history = history
@non_win_count = non_win_count
end
# @return [Boolean] True if the computer did not win the game
def play
while true
print_beans
move = get_move("YOUR MOVE")
home_pit = 6
computer_home_pit = 13
last_pit = perform_move(move, home_pit)
print_beans
break if game_over
if home_pit == last_pit
second_move = get_move("AGAIN")
perform_move(second_move, home_pit)
print_beans
break if game_over
end
computer_move, computer_last_pit = get_computer_move
print "MY MOVE IS #{computer_move - 6}"
break if game_over
if computer_last_pit == computer_home_pit
second_computer_move, _ = get_computer_move
print ",#{second_computer_move - 6}"
break if game_over
end
end
end_game
end
private
def game_over
@beans[0...6].all? { |b| b == 0 } || @beans[7...13].all? { |b| b == 0 }
end
# @return [Boolean] True if the computer did not win
def end_game
puts
puts "GAME OVER"
difference = @beans[6] - @beans[13]
if difference < 0
puts "I WIN BY #{-difference} POINTS"
return
end
puts "YOU WIN BY #{difference} POINTS" if difference > 0
puts "DRAWN GAME" if difference == 0
difference >= 0
end
# @param [Integer] move
# @param [Integer] home_pit
def perform_move(move, home_pit)
last_pit = distribute_beans(move, home_pit)
update_history(move)
last_pit
end
def update_history(current_move)
k = current_move % 7
@turn_counter += 1
# Add the move to the history
@history[@non_win_count] = @history[@non_win_count] * 6 + k if @turn_counter < 9
end
def print_beans
puts
# Print computer beans
print ' ' * 3
@beans[7...13].reverse.each { |bean_count| print_bean(bean_count) }
puts
# Print home beans
print_bean(@beans[13])
print ' ' * 23
print_number(@beans[6]) # This is not print_bean in line with the original version
puts
# Print player beans
print ' ' * 3
@beans[0...6].each { |bean_count| print_bean(bean_count) }
puts
puts
end
def get_move(prompt)
move = get_integer_input(prompt)
while move < 1 || move > 6 || @beans[move - 1] == 0
puts "ILLEGAL MOVE"
move = get_integer_input("AGAIN")
end
move - 1
end
def distribute_beans(start_pit, home_pit, beans = @beans)
beans_to_distribute = beans[start_pit]
beans[start_pit] = 0
current_pit = start_pit
(0...beans_to_distribute).each do
current_pit = (current_pit + 1) % beans.size
beans[current_pit] += 1
end
# If the last pit was empty before we put a bean in it (and it's not a scoring pit), add beans to score
if beans[current_pit] == 1 && current_pit != 6 && current_pit != 13 && beans[12 - current_pit] != 0
beans[home_pit] = beans[home_pit] + beans[12 - current_pit] + 1
beans[current_pit] = 0
beans[12 - current_pit] = 0
end
current_pit
end
def print_bean(bean_count)
print ' ' if bean_count < 10
print_number(bean_count)
end
def get_integer_input(prompt)
integer_value = nil
input_values = input(prompt)
while integer_value.nil?
print '!EXTRA INPUT IGNORED' if (input_values.size > 1)
value = input_values.first
begin
integer_value = Integer(value)
rescue
puts '!NUMBER EXPECTED - RETRY INPUT LINE'
input_values = input('')
end
end
integer_value
end
def get_computer_move
d = -99
home_pit = 13
chosen_move = 7
# Test all possible moves
(7...13).each do |move_under_test|
# Create a copy of the beans to test against
beans_copy = @beans.dup
# If the move is not legal, skip it
next if beans_copy[move_under_test] == 0
# Determine the best response the player may make to this move
player_max_score = 0
# Make the move under test against the copy
distribute_beans(move_under_test, home_pit, beans_copy)
# Test every player response
(0...6).each do |i|
# Skip the move if it would be illegal
next if beans_copy[i] == 0
# Determine the last
landing_with_overflow = beans_copy[i] + i
# If landing > 13 the player has put a bean in both home pits
player_move_score = (landing_with_overflow > 14) ? 1 : 0
# Find the actual pit
landing = landing_with_overflow % 14
# If the landing pit is empty, the player will steal beans
if beans_copy[landing] == 0 && landing != 6 && landing != 13
player_move_score = beans_copy[12 - landing] + player_move_score
end
# Update the max score if this move is the best player move
player_max_score = player_move_score if player_move_score > player_max_score
end
# Final score for move is computer score, minus the player's score and any player gains from their best move
final_score = beans_copy[13] - beans_copy[6] - player_max_score
if @turn_counter < 9
k = move_under_test % 7
(0...@non_win_count).each do |i|
# Penalise move if it was used in a losing game
final_score = final_score - 2 if @history[@non_win_count] * 6 + k == ((Float(@history[i]) / 6 ** (7 - @turn_counter)) + 0.1).floor
end
end
# Choose the move if it is the best move found so far
if final_score >= d
chosen_move = move_under_test
d = final_score
end
end
last_pit = perform_move(chosen_move, home_pit)
[chosen_move, last_pit]
end
end
puts 'AWARI'.center(80)
puts 'CREATIVE COMPUTING MORRISTOWN, NEW JERSEY'.center(80)
# Initialise stable variables
history = Array.new(50)
non_win_count = 0
# APPLICATION LOOP
while true
puts
puts
history[non_win_count] = 0
game = Game.new(history, non_win_count)
computer_didnt_win = game.play
non_win_count += 1 if computer_didnt_win
end
+22
View File
@@ -0,0 +1,22 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Awari", "Awari.vbproj", "{718AECEB-CC24-49C8-B620-B2F93E021C51}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{718AECEB-CC24-49C8-B620-B2F93E021C51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{718AECEB-CC24-49C8-B620-B2F93E021C51}.Debug|Any CPU.Build.0 = Debug|Any CPU
{718AECEB-CC24-49C8-B620-B2F93E021C51}.Release|Any CPU.ActiveCfg = Release|Any CPU
{718AECEB-CC24-49C8-B620-B2F93E021C51}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
+8
View File
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<RootNamespace>Awari</RootNamespace>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>16.9</LangVersion>
</PropertyGroup>
</Project>
-1
View File
@@ -3,7 +3,6 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<RootNamespace>BasicComputerGames.Bagels</RootNamespace>
</PropertyGroup>
<ItemGroup>
+22
View File
@@ -0,0 +1,22 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bagels", "Bagels.csproj", "{2FC5F33F-2C4B-4707-94E5-3C9B2B633EFE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2FC5F33F-2C4B-4707-94E5-3C9B2B633EFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2FC5F33F-2C4B-4707-94E5-3C9B2B633EFE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2FC5F33F-2C4B-4707-94E5-3C9B2B633EFE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2FC5F33F-2C4B-4707-94E5-3C9B2B633EFE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
+22
View File
@@ -0,0 +1,22 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Bagels", "Bagels.vbproj", "{913FE7A8-B481-4EB3-8938-566BEAD5B0F2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{913FE7A8-B481-4EB3-8938-566BEAD5B0F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{913FE7A8-B481-4EB3-8938-566BEAD5B0F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{913FE7A8-B481-4EB3-8938-566BEAD5B0F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{913FE7A8-B481-4EB3-8938-566BEAD5B0F2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
+8
View File
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<RootNamespace>Bagels</RootNamespace>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>16.9</LangVersion>
</PropertyGroup>
</Project>
+5 -5
View File
@@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31321.278
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "banner", "banner.csproj", "{9E24FA30-F2AC-4BF3-ADFB-92D3F796561C}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Banner", "Banner.csproj", "{7E8612AB-AFFD-4F72-855F-8172786F28FD}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -11,10 +11,10 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9E24FA30-F2AC-4BF3-ADFB-92D3F796561C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9E24FA30-F2AC-4BF3-ADFB-92D3F796561C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9E24FA30-F2AC-4BF3-ADFB-92D3F796561C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9E24FA30-F2AC-4BF3-ADFB-92D3F796561C}.Release|Any CPU.Build.0 = Release|Any CPU
{7E8612AB-AFFD-4F72-855F-8172786F28FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7E8612AB-AFFD-4F72-855F-8172786F28FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7E8612AB-AFFD-4F72-855F-8172786F28FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7E8612AB-AFFD-4F72-855F-8172786F28FD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+92
View File
@@ -0,0 +1,92 @@
#!/usr/bin/env ruby
# Banner
# reinterpreted from BASIC by stephan.com
# this implementation diverges from the original in some notable
# ways, but maintains the same font definition as before as well
# as the same somewhat bizarre way of interpreting it. It would
# be more efficient to redesign the font to allow `"%09b" % row`
# and then some substitutions.
FONT = {
' ' => [0, 0, 0, 0, 0, 0, 0].freeze,
'!' => [1, 1, 1, 384, 1, 1, 1].freeze,
'*' => [69, 41, 17, 512, 17, 41, 69].freeze,
'.' => [1, 1, 129, 449, 129, 1, 1].freeze,
'0' => [57, 69, 131, 258, 131, 69, 57].freeze,
'1' => [0, 0, 261, 259, 512, 257, 257].freeze,
'2' => [261, 387, 322, 290, 274, 267, 261].freeze,
'3' => [66, 130, 258, 274, 266, 150, 100].freeze,
'4' => [33, 49, 41, 37, 35, 512, 33].freeze,
'5' => [160, 274, 274, 274, 274, 274, 226].freeze,
'6' => [194, 291, 293, 297, 305, 289, 193].freeze,
'7' => [258, 130, 66, 34, 18, 10, 8].freeze,
'8' => [69, 171, 274, 274, 274, 171, 69].freeze,
'9' => [263, 138, 74, 42, 26, 10, 7].freeze,
'=' => [41, 41, 41, 41, 41, 41, 41].freeze,
'?' => [5, 3, 2, 354, 18, 11, 5].freeze,
'a' => [505, 37, 35, 34, 35, 37, 505].freeze,
'b' => [512, 274, 274, 274, 274, 274, 239].freeze,
'c' => [125, 131, 258, 258, 258, 131, 69].freeze,
'd' => [512, 258, 258, 258, 258, 131, 125].freeze,
'e' => [512, 274, 274, 274, 274, 258, 258].freeze,
'f' => [512, 18, 18, 18, 18, 2, 2].freeze,
'g' => [125, 131, 258, 258, 290, 163, 101].freeze,
'h' => [512, 17, 17, 17, 17, 17, 512].freeze,
'i' => [258, 258, 258, 512, 258, 258, 258].freeze,
'j' => [65, 129, 257, 257, 257, 129, 128].freeze,
'k' => [512, 17, 17, 41, 69, 131, 258].freeze,
'l' => [512, 257, 257, 257, 257, 257, 257].freeze,
'm' => [512, 7, 13, 25, 13, 7, 512].freeze,
'n' => [512, 7, 9, 17, 33, 193, 512].freeze,
'o' => [125, 131, 258, 258, 258, 131, 125].freeze,
'p' => [512, 18, 18, 18, 18, 18, 15].freeze,
'q' => [125, 131, 258, 258, 322, 131, 381].freeze,
'r' => [512, 18, 18, 50, 82, 146, 271].freeze,
's' => [69, 139, 274, 274, 274, 163, 69].freeze,
't' => [2, 2, 2, 512, 2, 2, 2].freeze,
'u' => [128, 129, 257, 257, 257, 129, 128].freeze,
'v' => [64, 65, 129, 257, 129, 65, 64].freeze,
'w' => [256, 257, 129, 65, 129, 257, 256].freeze,
'x' => [388, 69, 41, 17, 41, 69, 388].freeze,
'y' => [8, 9, 17, 481, 17, 9, 8].freeze,
'z' => [386, 322, 290, 274, 266, 262, 260].freeze
}.freeze
puts 'horizontal'
x = gets.strip.to_i
puts 'vertical'
y = gets.strip.to_i
puts 'centered'
centered = gets.strip.downcase.chars.first == 'y'
puts 'character ("all" for character being printed)'
fill = gets.strip.downcase
puts 'statement'
statement = gets.strip.downcase
all = (fill.downcase == 'all')
lenxs = all ? 1 : fill.length
start = 1
start += (63 - 4.5 * y) / lenxs if centered
statement.each_char do |char|
next puts "\n" * 7 * x if char == ' '
xs = all ? char : fill
FONT[char].each do |su|
print ' ' * start
8.downto(0) do |k|
if (1 << k) < su
print xs * y
su -= (1 << k)
else
print ' ' * (y * lenxs)
end
end
puts
end
(2 * x).times { puts }
end
75.times { puts }
+5 -5
View File
@@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31321.278
MinimumVisualStudioVersion = 10.0.40219.1
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "banner", "banner.vbproj", "{1738D297-A04C-4E6E-8219-D9E72982C39D}"
Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "Banner", "Banner.vbproj", "{091ABE13-3E70-4848-B836-592F725915A3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -11,10 +11,10 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1738D297-A04C-4E6E-8219-D9E72982C39D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1738D297-A04C-4E6E-8219-D9E72982C39D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1738D297-A04C-4E6E-8219-D9E72982C39D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1738D297-A04C-4E6E-8219-D9E72982C39D}.Release|Any CPU.Build.0 = Release|Any CPU
{091ABE13-3E70-4848-B836-592F725915A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{091ABE13-3E70-4848-B836-592F725915A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{091ABE13-3E70-4848-B836-592F725915A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{091ABE13-3E70-4848-B836-592F725915A3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+2 -1
View File
@@ -2,8 +2,9 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<RootNamespace>banner</RootNamespace>
<RootNamespace>Banner</RootNamespace>
<TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>16.9</LangVersion>
</PropertyGroup>
</Project>
+9
View File
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>10</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
+22
View File
@@ -0,0 +1,22 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Basketball", "Basketball.csproj", "{00D03FB3-B485-480F-B14D-746371BDE08B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{00D03FB3-B485-480F-B14D-746371BDE08B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{00D03FB3-B485-480F-B14D-746371BDE08B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{00D03FB3-B485-480F-B14D-746371BDE08B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{00D03FB3-B485-480F-B14D-746371BDE08B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
+469
View File
@@ -0,0 +1,469 @@
import java.lang.Math;
import java.util.*;
import java.util.Scanner;
/* The basketball class is a computer game that allows you to play as
Dartmouth College's captain and playmaker
The game uses set probabilites to simulate outcomes of each posession
You are able to choose your shot types as well as defensive formations */
public class Basketball {
int time = 0;
int[] score = {0, 0};
double defense = -1;
List<Double> defense_choices = Arrays.asList(6.0, 6.5, 7.0, 7.5);
int shot = -1;
List<Integer> shot_choices = Arrays.asList(0, 1, 2, 3, 4);
double opponent_chance = 0;
String opponent = null;
public Basketball() {
// Explains the keyboard inputs
System.out.println("\t\t\t Basketball");
System.out.println("\t Creative Computing Morristown, New Jersey\n\n\n");
System.out.println("This is Dartmouth College basketball. ");
System.out.println("Υou will be Dartmouth captain and playmaker.");
System.out.println("Call shots as follows:");
System.out.println("1. Long (30ft.) Jump Shot; 2. Short (15 ft.) Jump Shot; "
+ "3. Lay up; 4. Set Shot");
System.out.println("Both teams will use the same defense. Call Defense as follows:");
System.out.println("6. Press; 6.5 Man-to-Man; 7. Zone; 7.5 None.");
System.out.println("To change defense, just type 0 as your next shot.");
System.out.print("Your starting defense will be? ");
Scanner scanner = new Scanner(System.in); // creates a scanner
// takes input for a defense
if (scanner.hasNextDouble()) {
defense = scanner.nextDouble();
}
else {
scanner.next();
}
// makes sure that input is legal
while (!defense_choices.contains(defense)) {
System.out.print("Your new defensive allignment is? ");
if (scanner.hasNextDouble()) {
defense = scanner.nextDouble();
}
else {
scanner.next();
continue;
}
}
// takes input for opponent's name
System.out.print("\nChoose your opponent? ");
opponent = scanner.next();
start_of_period();
}
// adds points to the score
// team can take 0 or 1, for opponent or Dartmouth, respectively
private void add_points(int team, int points) {
score[team] += points;
print_score();
}
private void ball_passed_back() {
System.out.print("Ball passed back to you. ");
dartmouth_ball();
}
// change defense, called when the user enters 0 for their shot
private void change_defense() {
defense = -1;
Scanner scanner = new Scanner(System.in); // creates a scanner
while (!defense_choices.contains(defense)) {
System.out.println("Your new defensive allignment is? ");
if (scanner.hasNextDouble()) {
defense = (double)(scanner.nextDouble());
}
else {
continue;
}
}
dartmouth_ball();
}
// simulates two foul shots for a player and adds the points
private void foul_shots(int team) {
System.out.println("Shooter fouled. Two shots.");
if (Math.random() > .49) {
if (Math.random() > .75) {
System.out.println("Both shots missed.");
}
else {
System.out.println("Shooter makes one shot and misses one.");
score[team] += 1;
}
}
else {
System.out.println("Shooter makes both shots.");
score[team] += 2;
}
print_score();
}
// called when time = 50, starts a new period
private void halftime() {
System.out.println("\n ***** End of first half *****\n");
print_score();
start_of_period();
}
// prints the current score
private void print_score() {
System.out.println("Score: " + score[1] + " to " + score[0] + "\n");
}
// simulates a center jump for posession at the beginning of a period
private void start_of_period() {
System.out.println("Center jump");
if (Math.random() > .6) {
System.out.println("Dartmouth controls the tap.\n");
dartmouth_ball();
}
else {
System.out.println(opponent + " controls the tap.\n");
opponent_ball();
}
}
// called when t = 92
private void two_minute_warning() {
System.out.println(" *** Two minutes left in the game ***");
}
// called when the user enters 1 or 2 for their shot
private void dartmouth_jump_shot() {
time ++;
if (time == 50) {
halftime();
}
else if (time == 92) {
two_minute_warning();
}
System.out.println("Jump Shot.");
// simulates chances of different possible outcomes
if (Math.random() > .341 * defense / 8) {
if (Math.random() > .682 * defense / 8) {
if (Math.random() > .782 * defense / 8) {
if (Math.random() > .843 * defense / 8) {
System.out.println("Charging foul. Dartmouth loses ball.\n");
opponent_ball();
}
else {
// player is fouled
foul_shots(1);
opponent_ball();
}
}
else {
if (Math.random() > .5) {
System.out.println("Shot is blocked. Ball controlled by " +
opponent + ".\n");
opponent_ball();
}
else {
System.out.println("Shot is blocked. Ball controlled by Dartmouth.");
dartmouth_ball();
}
}
}
else {
System.out.println("Shot is off target.");
if (defense / 6 * Math.random() > .45) {
System.out.println("Rebound to " + opponent + "\n");
opponent_ball();
}
else {
System.out.println("Dartmouth controls the rebound.");
if (Math.random() > .4) {
if (defense == 6 && Math.random() > .6) {
System.out.println("Pass stolen by " + opponent
+ ", easy lay up");
add_points(0, 2);
dartmouth_ball();
}
else {
// ball is passed back to you
ball_passed_back();
}
}
else {
System.out.println("");
dartmouth_non_jump_shot();
}
}
}
}
else {
System.out.println("Shot is good.");
add_points(1, 2);
opponent_ball();
}
}
// called when the user enters 0, 3, or 4
// lay up, set shot, or defense change
private void dartmouth_non_jump_shot() {
time ++;
if (time == 50) {
halftime();
}
else if (time == 92) {
two_minute_warning();
}
if (shot == 4) {
System.out.println("Set shot.");
}
else if (shot == 3) {
System.out.println("Lay up.");
}
else if (shot == 0) {
change_defense();
}
// simulates different outcomes after a lay up or set shot
if (7/defense*Math.random() > .4) {
if (7/defense*Math.random() > .7) {
if (7/defense*Math.random() > .875) {
if (7/defense*Math.random() > .925) {
System.out.println("Charging foul. Dartmouth loses the ball.\n");
opponent_ball();
}
else {
System.out.println("Shot blocked. " + opponent + "'s ball.\n");
opponent_ball();
}
}
else {
foul_shots(1);
opponent_ball();
}
}
else {
System.out.println("Shot is off the rim.");
if (Math.random() > 2/3) {
System.out.println("Dartmouth controls the rebound.");
if (Math.random() > .4) {
System.out.println("Ball passed back to you.\n");
dartmouth_ball();
}
else {
dartmouth_non_jump_shot();
}
}
else {
System.out.println(opponent + " controls the rebound.\n");
opponent_ball();
}
}
}
else {
System.out.println("Shot is good. Two points.");
add_points(1, 2);
opponent_ball();
}
}
// plays out a Dartmouth posession, starting with your choice of shot
private void dartmouth_ball() {
Scanner scanner = new Scanner(System.in); // creates a scanner
System.out.print("Your shot? ");
shot = -1;
if (scanner.hasNextInt()) {
shot = scanner.nextInt();
}
else {
System.out.println("");
scanner.next();
}
while (!shot_choices.contains(shot)) {
System.out.print("Incorrect answer. Retype it. Your shot?");
if (scanner.hasNextInt()) {
shot = scanner.nextInt();
}
else {
System.out.println("");
scanner.next();
}
}
if (time < 100 || Math.random() < .5) {
if (shot == 1 || shot == 2) {
dartmouth_jump_shot();
}
else {
dartmouth_non_jump_shot();
}
}
else {
if (score[0] != score[1]) {
System.out.println("\n ***** End Of Game *****");
System.out.println("Final Score: Dartmouth: " + score[1] + " "
+ opponent + ": " + score[0]);
System.exit(0);
}
else {
System.out.println("\n ***** End Of Second Half *****");
System.out.println("Score at end of regulation time:");
System.out.println(" Dartmouth: " + score[1] + " " +
opponent + ": " + score[0]);
System.out.println("Begin two minute overtime period");
time = 93;
start_of_period();
}
}
}
// simulates the opponents jumpshot
private void opponent_jumpshot() {
System.out.println("Jump Shot.");
if (8/defense*Math.random() > .35) {
if (8/defense*Math.random() > .75) {
if (8/defense*Math.random() > .9) {
System.out.println("Offensive foul. Dartmouth's ball.\n");
dartmouth_ball();
}
else {
foul_shots(0);
dartmouth_ball();
}
}
else {
System.out.println("Shot is off the rim.");
if (defense/6*Math.random() > .5) {
System.out.println(opponent + " controls the rebound.");
if (defense == 6) {
if (Math.random() > .75) {
System.out.println("Ball stolen. Easy lay up for Dartmouth.");
add_points(1, 2);
opponent_ball();
}
else {
if (Math.random() > .5) {
System.out.println("");
opponent_non_jumpshot();
}
else {
System.out.println("Pass back to " + opponent +
" guard.\n");
opponent_ball();
}
}
}
else {
if (Math.random() > .5) {
opponent_non_jumpshot();
}
else {
System.out.println("Pass back to " + opponent +
" guard.\n");
opponent_ball();
}
}
}
else {
System.out.println("Dartmouth controls the rebound.\n");
dartmouth_ball();
}
}
}
else {
System.out.println("Shot is good.");
add_points(0, 2);
dartmouth_ball();
}
}
// simulates opponents lay up or set shot
private void opponent_non_jumpshot() {
if (opponent_chance > 3) {
System.out.println("Set shot.");
}
else {
System.out.println("Lay up");
}
if (7/defense*Math.random() > .413) {
System.out.println("Shot is missed.");
if (defense/6*Math.random() > .5) {
System.out.println(opponent + " controls the rebound.");
if (defense == 6) {
if (Math.random() > .75) {
System.out.println("Ball stolen. Easy lay up for Dartmouth.");
add_points(1, 2);
opponent_ball();
}
else {
if (Math.random() > .5) {
System.out.println("");
opponent_non_jumpshot();
}
else {
System.out.println("Pass back to " + opponent +
" guard.\n");
opponent_ball();
}
}
}
else {
if (Math.random() > .5) {
System.out.println("");
opponent_non_jumpshot();
}
else {
System.out.println("Pass back to " + opponent + " guard\n");
opponent_ball();
}
}
}
else {
System.out.println("Dartmouth controls the rebound.\n");
dartmouth_ball();
}
}
else {
System.out.println("Shot is good.");
add_points(0, 2);
dartmouth_ball();
}
}
// simulates an opponents possesion
// #randomly picks jump shot or lay up / set shot.
private void opponent_ball() {
time ++;
if (time == 50) {
halftime();
}
opponent_chance = 10/4*Math.random()+1;
if (opponent_chance > 2) {
opponent_non_jumpshot();
}
else {
opponent_jumpshot();
}
}
public static void main(String[] args) {
Basketball new_game = new Basketball();
}
}
+343
View File
@@ -0,0 +1,343 @@
import random
# The basketball class is a computer game that allows you to play as
# Dartmouth College's captain and playmaker
# The game uses set probabilites to simulate outcomes of each posession
# You are able to choose your shot types as well as defensive formations
class Basketball():
def __init__(self):
self.time = 0
self.score = [0, 0] # first value is opponents score, second is home
self.defense = None
self.defense_choices = [6, 6.5, 7, 7.5]
self.shot = None
self.shot_choices = [0, 1, 2, 3, 4]
self.z1 = None
# Explains the keyboard inputs
print("\t\t\t Basketball")
print("\t Creative Computing Morristown, New Jersey\n\n\n")
print("This is Dartmouth College basketball. ")
print("Υou will be Dartmouth captain and playmaker.")
print("Call shots as follows:")
print("1. Long (30ft.) Jump Shot; 2. Short (15 ft.) Jump Shot; "
+ "3. Lay up; 4. Set Shot")
print("Both teams will use the same defense. Call Defense as follows:")
print("6. Press; 6.5 Man-to-Man; 7. Zone; 7.5 None.")
print("To change defense, just type 0 as your next shot.")
print("Your starting defense will be? ", end='')
# takes input for a defense
try:
self.defense = float(input())
except ValueError:
self.defense = None
# if the input wasn't a valid defense, takes input again
while self.defense not in self.defense_choices:
print("Your new defensive allignment is? ", end='')
try:
self.defense = float(input())
except ValueError:
continue
# takes input for opponent's name
print("\nChoose your opponent? ", end='')
self.opponent = input()
self.start_of_period()
# adds points to the score
# team can take 0 or 1, for opponent or Dartmouth, respectively
def add_points(self, team, points):
self.score[team] += points
self.print_score()
def ball_passed_back(self):
print("Ball passed back to you. ", end='')
self.dartmouth_ball()
# change defense, called when the user enters 0 for their shot
def change_defense(self):
self.defense = None
while self.defense not in self.defense_choices:
print("Your new defensive allignment is? ")
try:
self.defense = float(input())
except ValueError:
continue
self.dartmouth_ball()
# simulates two foul shots for a player and adds the points
def foul_shots(self, team):
print("Shooter fouled. Two shots.")
if random.random() > .49:
if random.random() > .75:
print("Both shots missed.")
else:
print("Shooter makes one shot and misses one.")
self.score[team] += 1
else:
print("Shooter makes both shots.")
self.score[team] += 2
self.print_score()
# called when t = 50, starts a new period
def halftime(self):
print("\n ***** End of first half *****\n")
self.print_score()
self.start_of_period()
# prints the current score
def print_score(self):
print("Score: " + str(self.score[1])
+ " to " + str(self.score[0]) + "\n")
# simulates a center jump for posession at the beginning of a period
def start_of_period(self):
print("Center jump")
if random.random() > .6:
print("Dartmouth controls the tap.\n")
self.dartmouth_ball()
else:
print(self.opponent + " controls the tap.\n")
self.opponent_ball()
# called when t = 92
def two_minute_warning(self):
print(" *** Two minutes left in the game ***")
# called when the user enters 1 or 2 for their shot
def dartmouth_jump_shot(self):
self.time += 1
if self.time == 50:
self.halftime()
elif self.time == 92:
self.two_minute_warning()
print("Jump Shot.")
# simulates chances of different possible outcomes
if random.random() > .341 * self.defense / 8:
if random.random() > .682 * self.defense / 8:
if random.random() > .782 * self.defense / 8:
if random.random() > .843 * self.defense / 8:
print("Charging foul. Dartmouth loses ball.\n")
self.opponent_ball()
else:
# player is fouled
self.foul_shots(1)
self.opponent_ball()
else:
if random.random() > .5:
print("Shot is blocked. Ball controlled by " +
self.opponent + ".\n")
self.opponent_ball()
else:
print("Shot is blocked. Ball controlled by Dartmouth.")
self.dartmouth_ball()
else:
print("Shot is off target.")
if self.defense / 6 * random.random() > .45:
print("Rebound to " + self.opponent + "\n")
self.opponent_ball()
else:
print("Dartmouth controls the rebound.")
if random.random() > .4:
if self.defense == 6 and random.random() > .6:
print("Pass stolen by " + self.opponent
+ ", easy lay up")
self.add_points(0, 2)
self.dartmouth_ball()
else:
# ball is passed back to you
self.ball_passed_back()
else:
print("")
self.dartmouth_non_jump_shot()
else:
print("Shot is good.")
self.add_points(1, 2)
self.opponent_ball()
# called when the user enters 0, 3, or 4
# lay up, set shot, or defense change
def dartmouth_non_jump_shot(self):
self.time += 1
if self.time == 50:
self.halftime()
elif self.time == 92:
self.two_minute_warning()
if self.shot == 4:
print("Set shot.")
elif self.shot == 3:
print("Lay up.")
elif self.shot == 0:
self.change_defense()
# simulates different outcomes after a lay up or set shot
if 7/self.defense*random.random() > .4:
if 7/self.defense*random.random() > .7:
if 7/self.defense*random.random() > .875:
if 7/self.defense*random.random() > .925:
print("Charging foul. Dartmouth loses the ball.\n")
self.opponent_ball()
else:
print("Shot blocked. " + self.opponent + "'s ball.\n")
self.opponent_ball()
else:
self.foul_shots(1)
self.opponent_ball()
else:
print("Shot is off the rim.")
if random.random() > 2/3:
print("Dartmouth controls the rebound.")
if random.random() > .4:
print("Ball passed back to you.\n")
self.dartmouth_ball()
else:
self.dartmouth_non_jump_shot()
else:
print(self.opponent + " controls the rebound.\n")
self.opponent_ball()
else:
print("Shot is good. Two points.")
self.add_points(1, 2)
self.opponent_ball()
# plays out a Dartmouth posession, starting with your choice of shot
def dartmouth_ball(self):
print("Your shot? ", end='')
self.shot = None
try:
self.shot = int(input())
except ValueError:
self.shot = None
while self.shot not in self.shot_choices:
print("Incorrect answer. Retype it. Your shot? ", end='')
try:
self.shot = int(input())
except:
continue
if self.time < 100 or random.random() < .5:
if self.shot == 1 or self.shot == 2:
self.dartmouth_jump_shot()
else:
self.dartmouth_non_jump_shot()
else:
if self.score[0] != self.score[1]:
print("\n ***** End Of Game *****")
print("Final Score: Dartmouth: " + str(self.score[1]) + " "
+ self.opponent + ": " + str(self.score[0]))
else:
print("\n ***** End Of Second Half *****")
print("Score at end of regulation time:")
print(" Dartmouth: " + str(self.score[1]) + " " +
self.opponent + ": " + str(self.score[0]))
print("Begin two minute overtime period")
self.time = 93
self.start_of_period()
# simulates the opponents jumpshot
def opponent_jumpshot(self):
print("Jump Shot.")
if 8/self.defense*random.random() > .35:
if 8/self.defense*random.random() > .75:
if 8/self.defense*random.random() > .9:
print("Offensive foul. Dartmouth's ball.\n")
self.dartmouth_ball()
else:
self.foul_shots(0)
self.dartmouth_ball()
else:
print("Shot is off the rim.")
if self.defense/6*random.random() > .5:
print(self.opponent + " controls the rebound.")
if self.defense == 6:
if random.random() > .75:
print("Ball stolen. Easy lay up for Dartmouth.")
self.add_points(1, 2)
self.opponent_ball()
else:
if random.random() > .5:
print("")
self.opponent_non_jumpshot()
else:
print("Pass back to " + self.opponent +
" guard.\n")
self.opponent_ball()
else:
if random.random() > .5:
self.opponent_non_jumpshot()
else:
print("Pass back to " + self.opponent +
" guard.\n")
self.opponent_ball()
else:
print("Dartmouth controls the rebound.\n")
self.dartmouth_ball()
else:
print("Shot is good.")
self.add_points(0, 2)
self.dartmouth_ball()
# simulates opponents lay up or set shot
def opponent_non_jumpshot(self):
if self.z1 > 3:
print("Set shot.")
else:
print("Lay up")
if 7/self.defense*random.random() > .413:
print("Shot is missed.")
if self.defense/6*random.random() > .5:
print(self.opponent + " controls the rebound.")
if self.defense == 6:
if random.random() > .75:
print("Ball stolen. Easy lay up for Dartmouth.")
self.add_points(1, 2)
self.opponent_ball()
else:
if random.random() > .5:
print("")
self.opponent_non_jumpshot()
else:
print("Pass back to " + self.opponent +
" guard.\n")
self.opponent_ball()
else:
if random.random() > .5:
print("")
self.opponent_non_jumpshot()
else:
print("Pass back to " + self.opponent + " guard\n")
self.opponent_ball()
else:
print("Dartmouth controls the rebound.\n")
self.dartmouth_ball()
else:
print("Shot is good.")
self.add_points(0, 2)
self.dartmouth_ball()
# simulates an opponents possesion
# #randomly picks jump shot or lay up / set shot.
def opponent_ball(self):
self.time += 1
if self.time == 50:
self.halftime()
self.z1 = 10/4*random.random()+1
if self.z1 > 2:
self.opponent_non_jumpshot()
else:
self.opponent_jumpshot()
new_game = Basketball()
+22
View File
@@ -0,0 +1,22 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Basketball", "Basketball.vbproj", "{09C533F2-4874-4BA4-9F80-BBE9E8E17456}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{09C533F2-4874-4BA4-9F80-BBE9E8E17456}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{09C533F2-4874-4BA4-9F80-BBE9E8E17456}.Debug|Any CPU.Build.0 = Debug|Any CPU
{09C533F2-4874-4BA4-9F80-BBE9E8E17456}.Release|Any CPU.ActiveCfg = Release|Any CPU
{09C533F2-4874-4BA4-9F80-BBE9E8E17456}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
+8
View File
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<RootNamespace>Basketball</RootNamespace>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>16.9</LangVersion>
</PropertyGroup>
</Project>
+3 -3
View File
@@ -1,9 +1,9 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31321.278
# Visual Studio Version 17
VisualStudioVersion = 17.0.32014.148
MinimumVisualStudioVersion = 10.0.40219.1
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "batnum", "batnum.vbproj", "{D577E429-F84D-4E84-86E7-E6526CFD5FD9}"
Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "Batnum", "Batnum.vbproj", "{D577E429-F84D-4E84-86E7-E6526CFD5FD9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
+2 -1
View File
@@ -2,8 +2,9 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<RootNamespace>batnum</RootNamespace>
<RootNamespace>Batnum</RootNamespace>
<TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>16.9</LangVersion>
</PropertyGroup>
</Project>
+1
View File
@@ -0,0 +1 @@
*~
+168
View File
@@ -0,0 +1,168 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Random;
import java.util.function.Predicate;
import java.text.NumberFormat;
/* This class holds the game state and the game logic */
public class Battle {
/* parameters of the game */
private int seaSize;
private int[] sizes;
private int[] counts;
/* The game setup - the ships and the sea */
private ArrayList<Ship> ships;
private Sea sea;
/* game state counts */
private int[] losses; // how many of each type of ship have been sunk
private int hits; // how many hits the player has made
private int misses; // how many misses the player has made
// Names of ships of each size. The game as written has ships of size 3, 4 and 5 but
// can easily be modified. It makes no sense to have a ship of size zero though.
private static String NAMES_BY_SIZE[] = {
"error",
"size1",
"destroyer",
"cruiser",
"aircraft carrier",
"size5" };
// Entrypoint
public static void main(String args[]) {
Battle game = new Battle(6, // Sea is 6 x 6 tiles
new int[] { 2, 3, 4 }, // Ships are of sizes 2, 3, and 4
new int[] { 2, 2, 2 }); // there are two ships of each size
game.play();
}
public Battle(int scale, int[] shipSizes, int[] shipCounts) {
seaSize = scale;
sizes = shipSizes;
counts = shipCounts;
// validate parameters
if (seaSize < 4) throw new RuntimeException("Sea Size " + seaSize + " invalid, must be at least 4");
for (int sz : sizes) {
if ((sz < 1) || (sz > seaSize))
throw new RuntimeException("Ship has invalid size " + sz);
}
if (counts.length != sizes.length) {
throw new RuntimeException("Ship counts must match");
}
// Initialize game state
sea = new Sea(seaSize); // holds what ship if any occupies each tile
ships = new ArrayList<Ship>(); // positions and states of all the ships
losses = new int[counts.length]; // how many ships of each type have been sunk
// Build up the list of all the ships
int shipNumber = 1;
for (int type = 0; type < counts.length; ++type) {
for (int i = 0; i < counts[i]; ++i) {
ships.add(new Ship(shipNumber++, sizes[type]));
}
}
// When we put the ships in the sea, we put the biggest ones in first, or they might
// not fit
ArrayList<Ship> largestFirst = new ArrayList<>(ships);
Collections.sort(largestFirst, Comparator.comparingInt((Ship ship) -> ship.size()).reversed());
// place each ship into the sea
for (Ship ship : largestFirst) {
ship.placeRandom(sea);
}
}
public void play() {
System.out.println("The following code of the bad guys' fleet disposition\nhas been captured but not decoded:\n");
System.out.println(sea.encodedDump());
System.out.println("De-code it and use it if you can\nbut keep the de-coding method a secret.\n");
int lost = 0;
System.out.println("Start game");
Input input = new Input(seaSize);
try {
while (lost < ships.size()) { // the game continues while some ships remain unsunk
if (! input.readCoordinates()) { // ... unless there is no more input from the user
return;
}
// The computer thinks of the sea as a grid of rows, from top to bottom.
// However, the user will use X and Y coordinates, with Y going bottom to top
int row = seaSize - input.y();
int col = input.x() - 1;
if (sea.isEmpty(col, row)) {
++misses;
System.out.println("Splash! Try again.");
} else {
Ship ship = ships.get(sea.get(col, row) - 1);
if (ship.isSunk()) {
++misses;
System.out.println("There used to be a ship at that point, but you sunk it.");
System.out.println("Splash! Try again.");
} else if (ship.wasHit(col, row)) {
++misses;
System.out.println("You already put a hole in ship number " + ship.id());
System.out.println("Splash! Try again.");
} else {
ship.hit(col, row);
++hits;
System.out.println("A direct hit on ship number " + ship.id());
// If a ship was hit, we need to know whether it was sunk.
// If so, tell the player and update our counts
if (ship.isSunk()) {
++lost;
System.out.println("And you sunk it. Hurrah for the good guys.");
System.out.print("So far, the bad guys have lost ");
ArrayList<String> typeDescription = new ArrayList<>();
for (int i = 0 ; i < sizes.length; ++i) {
if (sizes[i] == ship.size()) {
++losses[i];
}
StringBuilder sb = new StringBuilder();
sb.append(losses[i]);
sb.append(" ");
sb.append(NAMES_BY_SIZE[sizes[i]]);
if (losses[i] != 1)
sb.append("s");
typeDescription.add(sb.toString());
}
System.out.println(String.join(", ", typeDescription));
double ratioNum = ((double)misses)/hits;
String ratio = NumberFormat.getInstance().format(ratioNum);
System.out.println("Your current splash/hit ratio is " + ratio);
if (lost == ships.size()) {
System.out.println("You have totally wiped out the bad guys' fleet");
System.out.println("With a final splash/hit ratio of " + ratio);
if (misses == 0) {
System.out.println("Congratulations - A direct hit every time.");
}
System.out.println("\n****************************\n");
}
}
}
}
}
}
catch (IOException e) {
// This should not happen running from console, but java requires us to check for it
System.err.println("System error.\n" + e);
}
}
}
+67
View File
@@ -0,0 +1,67 @@
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.text.NumberFormat;
// This class handles reading input from the player
// Each input is an x and y coordinate
// e.g. 5,3
public class Input {
private BufferedReader reader;
private NumberFormat parser;
private int scale; // size of the sea, needed to validate input
private boolean isQuit; // whether the input has ended
private int[] coords; // the last coordinates read
public Input(int seaSize) {
scale = seaSize;
reader = new BufferedReader(new InputStreamReader(System.in));
parser = NumberFormat.getIntegerInstance();
}
public boolean readCoordinates() throws IOException {
while (true) {
// Write a prompt
System.out.print("\nTarget x,y\n> ");
String inputLine = reader.readLine();
if (inputLine == null) {
// If the input stream is ended, there is no way to continue the game
System.out.println("\nGame quit\n");
isQuit = true;
return false;
}
// split the input into two fields
String[] fields = inputLine.split(",");
if (fields.length != 2) {
// has to be exactly two
System.out.println("Need two coordinates separated by ','");
continue;
}
coords = new int[2];
boolean error = false;
// each field should contain an integer from 1 to the size of the sea
try {
for (int c = 0 ; c < 2; ++c ) {
int val = Integer.parseInt(fields[c].strip());
if ((val < 1) || (val > scale)) {
System.out.println("Coordinates must be from 1 to " + scale);
error = true;
} else {
coords[c] = val;
}
}
}
catch (NumberFormatException ne) {
// this happens if the field is not a valid number
System.out.println("Coordinates must be numbers");
error = true;
}
if (!error) return true;
}
}
public int x() { return coords[0]; }
public int y() { return coords[1]; }
}
+60
View File
@@ -0,0 +1,60 @@
// Track the content of the sea
class Sea {
// the sea is a square grid of tiles. It is a one-dimensional array, and this
// class maps x and y coordinates to an array index
// Each tile is either empty (value of tiles at index is 0)
// or contains a ship (value of tiles at index is the ship number)
private int tiles[];
private int size;
public Sea(int make_size) {
size = make_size;
tiles = new int[size*size];
}
public int size() { return size; }
// This writes out a representation of the sea, but in a funny order
// The idea is to give the player the job of working it out
public String encodedDump() {
StringBuilder out = new StringBuilder();
for (int x = 0; x < size; ++x) {
for (int y = 0; y < size; ++y)
out.append(Integer.toString(get(x, y)));
out.append('\n');
}
return out.toString();
}
/* return true if x,y is in the sea and empty
* return false if x,y is occupied or is out of range
* Doing this in one method makes placing ships much easier
*/
public boolean isEmpty(int x, int y) {
if ((x<0)||(x>=size)||(y<0)||(y>=size)) return false;
return (get(x,y) == 0);
}
/* return the ship number, or zero if no ship.
* Unlike isEmpty(x,y), these other methods require that the
* coordinates passed be valid
*/
public int get(int x, int y) {
return tiles[index(x,y)];
}
public void set(int x, int y, int value) {
tiles[index(x, y)] = value;
}
// map the coordinates to the array index
private int index(int x, int y) {
if ((x < 0) || (x >= size))
throw new ArrayIndexOutOfBoundsException("Program error: x cannot be " + x);
if ((y < 0) || (y >= size))
throw new ArrayIndexOutOfBoundsException("Program error: y cannot be " + y);
return y*size + x;
}
}
+170
View File
@@ -0,0 +1,170 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Random;
import java.util.function.Predicate;
/** A single ship, with its position and where it has been hit */
class Ship {
// These are the four directions that ships can be in
public static final int ORIENT_E=0; // goes East from starting position
public static final int ORIENT_SE=1; // goes SouthEast from starting position
public static final int ORIENT_S=2; // goes South from starting position
public static final int ORIENT_SW=3; // goes SouthWest from starting position
private int id; // ship number
private int size; // how many tiles it occupies
private boolean placed; // whether this ship is in the sea yet
private boolean sunk; // whether this ship has been sunk
private ArrayList<Boolean> hits; // which tiles of the ship have been hit
private int startX; // starting position coordinates
private int startY;
private int orientX; // x and y deltas from each tile occupied to the next
private int orientY;
public Ship(int i, int sz) {
id = i; size = sz;
sunk = false; placed = false;
hits = new ArrayList<>(Collections.nCopies(size, false));
}
/** @returns the ship number */
public int id() { return id; }
/** @returns the ship size */
public int size() { return size; }
/* record the ship as having been hit at the given coordinates */
public void hit(int x, int y) {
// need to work out how many tiles from the ship's starting position the hit is at
// that can be worked out from the difference between the starting X coord and this one
// unless the ship runs N-S, in which case use the Y coord instead
int offset;
if (orientX != 0) {
offset = (x - startX) / orientX;
} else {
offset = (y - startY) / orientY;
}
hits.set(offset, true);
// if every tile of the ship has been hit, the ship is sunk
sunk = hits.stream().allMatch(Predicate.isEqual(true));
}
public boolean isSunk() { return sunk; }
// whether the ship has already been hit at the given coordinates
public boolean wasHit(int x, int y) {
int offset;
if (orientX != 0) {
offset = (x - startX) / orientX;
} else {
offset = (y - startY) / orientY;
}
return hits.get(offset);
};
// Place the ship in the sea.
// choose a random starting position, and a random direction
// if that doesn't fit, keep picking different positions and directions
public void placeRandom(Sea s) {
Random random = new Random();
for (int tries = 0 ; tries < 1000 ; ++tries) {
int x = random.nextInt(s.size());
int y = random.nextInt(s.size());
int orient = random.nextInt(4);
if (place(s, x, y, orient)) return;
}
throw new RuntimeException("Could not place any more ships");
}
// Attempt to fit the ship into the sea, starting from a given position and
// in a given direction
// This is by far the most complicated part of the program.
// It will start at the position provided, and attempt to occupy tiles in the
// requested direction. If it does not fit, either because of the edge of the
// sea, or because of ships already in place, it will try to extend the ship
// in the opposite direction instead. If that is not possible, it fails.
public boolean place(Sea s, int x, int y, int orient) {
if (placed) {
throw new RuntimeException("Program error - placed ship " + id + " twice");
}
switch(orient) {
case ORIENT_E: // east is increasing X coordinate
orientX = 1; orientY = 0;
break;
case ORIENT_SE: // southeast is increasing X and Y
orientX = 1; orientY = 1;
break;
case ORIENT_S: // south is increasing Y
orientX = 0; orientY = 1;
break;
case ORIENT_SW: // southwest is increasing Y but decreasing X
orientX = -1; orientY = 1;
break;
default:
throw new RuntimeException("Invalid orientation " + orient);
}
if (!s.isEmpty(x, y)) return false; // starting position is occupied - placing fails
startX = x; startY = y;
int tilesPlaced = 1;
int nextX = startX;
int nextY = startY;
while (tilesPlaced < size) {
if (extendShip(s, nextX, nextY, nextX + orientX, nextY + orientY)) {
// It is clear to extend the ship forwards
tilesPlaced += 1;
nextX = nextX + orientX;
nextY = nextY + orientY;
} else {
int backX = startX - orientX;
int backY = startY - orientY;
if (extendShip(s, startX, startY, backX, backY)) {
// We can move the ship backwards, so it can be one tile longer
tilesPlaced +=1;
startX = backX;
startY = backY;
} else {
// Could not make it longer or move it backwards
return false;
}
}
}
// Mark in the sea which tiles this ship occupies
for (int i = 0; i < size; ++i) {
int sx = startX + i * orientX;
int sy = startY + i * orientY;
s.set(sx, sy, id);
}
placed = true;
return true;
}
// Check whether a ship which already occupies the "from" coordinates,
// can also occupy the "to" coordinates.
// They must be within the sea area, empty, and not cause the ship to cross
// over another ship
private boolean extendShip(Sea s, int fromX, int fromY, int toX, int toY) {
if (!s.isEmpty(toX, toY)) return false; // no space
if ((fromX == toX)||(fromY == toY)) return true; // horizontal or vertical
// we can extend the ship without colliding, but we are going diagonally
// and it should not be possible for two ships to cross each other on
// opposite diagonals.
// check the two tiles that would cross us here - if either is empty, we are OK
// if they both contain different ships, we are OK
// but if they both contain the same ship, we are crossing!
int corner1 = s.get(fromX, toY);
int corner2 = s.get(toX, fromY);
if ((corner1 == 0) || (corner1 != corner2)) return true;
return false;
}
}
+167
View File
@@ -0,0 +1,167 @@
#!/usr/bin/env python3
from random import randrange
from typing import List, Tuple
PointType = Tuple[int, int]
VectorType = PointType
SeaType = Tuple[List[int], ...]
SEA_WIDTH = 6
DESTROYER_LENGTH = 2
CRUISER_LENGTH = 3
AIRCRAFT_CARRIER_LENGTH = 4
def random_vector() -> Tuple[int, int]:
while True:
vector = (randrange(-1, 2), randrange(-1, 2))
if vector == (0, 0):
# We can't have a zero vector, so try again
continue
return vector
def add_vector(point: PointType, vector: VectorType) -> PointType:
return (point[0] + vector[0], point[1] + vector[1])
def place_ship(sea: SeaType, size: int, code: int) -> None:
while True:
start = (randrange(1, SEA_WIDTH + 1), randrange(1, SEA_WIDTH + 1))
vector = random_vector()
# Get potential ship points
point = start
points = []
for _ in range(size):
point = add_vector(point, vector)
points.append(point)
if (not all([is_within_sea(point, sea) for point in points]) or
any([value_at(point, sea) for point in points])):
# ship out of bounds or crosses other ship, trying again
continue
# We found a valid spot, so actually place it now
for point in points:
set_value_at(code, point, sea)
break
def print_encoded_sea(sea: SeaType) -> None:
for x in range(len(sea)):
print(' '.join([str(sea[y][x]) for y in range(len(sea) - 1, -1, -1)]))
def is_within_sea(point: PointType, sea: SeaType) -> bool:
return (1 <= point[0] <= len(sea)) and (1 <= point[1] <= len(sea))
def has_ship(sea: SeaType, code: int) -> bool:
return any(code in row for row in sea)
def count_sunk(sea: SeaType, *codes: int) -> int:
return sum(not has_ship(sea, code) for code in codes)
def value_at(point: PointType, sea: SeaType) -> int:
return sea[point[1] - 1][point[0] -1]
def set_value_at(value: int, point: PointType, sea: SeaType) -> None:
sea[point[1] - 1][point[0] -1] = value
def get_next_target(sea: SeaType) -> PointType:
while True:
try:
guess = input('? ')
point = guess.split(',')
if len(point) != 2:
raise ValueError()
point = (int(point[0]), int(point[1]))
if not is_within_sea(point, sea):
raise ValueError()
return point
except ValueError:
print(f'INVALID. SPECIFY TWO NUMBERS FROM 1 TO {len(sea)}, SEPARATED BY A COMMA.')
def setup_ships(sea: SeaType):
place_ship(sea, DESTROYER_LENGTH, 1)
place_ship(sea, DESTROYER_LENGTH, 2)
place_ship(sea, CRUISER_LENGTH, 3)
place_ship(sea, CRUISER_LENGTH, 4)
place_ship(sea, AIRCRAFT_CARRIER_LENGTH, 5)
place_ship(sea, AIRCRAFT_CARRIER_LENGTH, 6)
def main() -> None:
sea = tuple(([0 for _ in range(SEA_WIDTH)] for _ in range(SEA_WIDTH)))
setup_ships(sea)
print(f'''
BATTLE
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
THE FOLLOWING CODE OF THE BAD GUYS' FLEET DISPOSITION
HAS BEEN CAPTURED BUT NOT DECODED:
''')
print_encoded_sea(sea)
print('''
DE-CODE IT AND USE IT IF YOU CAN
BUT KEEP THE DE-CODING METHOD A SECRET.
START GAME''')
splashes = 0
hits = 0
while True:
target = get_next_target(sea)
target_value = value_at(target, sea)
if target_value < 0:
print(f'YOU ALREADY PUT A HOLE IN SHIP NUMBER {abs(target_value)} AT THAT POINT.')
if target_value <= 0:
print('SPLASH! TRY AGAIN.')
splashes += 1
continue
print(f'A DIRECT HIT ON SHIP NUMBER {target_value}')
hits += 1
set_value_at(-target_value, target, sea)
if not has_ship(sea, target_value):
print('AND YOU SUNK IT. HURRAH FOR THE GOOD GUYS.')
print('SO FAR, THE BAD GUYS HAVE LOST')
print(f'{count_sunk(sea, 1, 2)} DESTROYER(S),',
f'{count_sunk(sea, 3, 4)} CRUISER(S),',
f'AND {count_sunk(sea, 5, 6)} AIRCRAFT CARRIER(S).')
if any(has_ship(sea, code) for code in range(1, 7)):
print(f'YOUR CURRENT SPLASH/HIT RATIO IS {splashes}/{hits}')
continue
print('YOU HAVE TOTALLY WIPED OUT THE BAD GUYS\' FLEET '
f'WITH A FINAL SPLASH/HIT RATIO OF {splashes}/{hits}')
if not splashes:
print('CONGRATULATIONS -- A DIRECT HIT EVERY TIME.')
print("\n****************************")
break
if __name__ == "__main__":
main()
+203
View File
@@ -0,0 +1,203 @@
#!/usr/bin/env python3
from dataclasses import dataclass
from random import randrange
DESTROYER_LENGTH = 2
CRUISER_LENGTH = 3
AIRCRAFT_CARRIER_LENGTH = 4
@dataclass(frozen=True)
class Point:
x: int
y: int
@classmethod
def random(cls, start: int, stop: int) -> 'Point':
return Point(randrange(start, stop), randrange(start, stop))
def __add__(self, vector: 'Vector') -> 'Point':
return Point(self.x + vector.x, self.y + vector.y)
@dataclass(frozen=True)
class Vector:
x: int
y: int
@staticmethod
def random() -> 'Vector':
return Vector(randrange(-1, 2, 2), randrange(-1, 2, 2))
def __mul__(self, factor: int) -> 'Vector':
return Vector(self.x * factor, self.y * factor)
class Sea:
WIDTH = 6
def __init__(self):
self._graph = tuple(([0 for _ in range(self.WIDTH)] for _ in range(self.WIDTH)))
def _validate_item_indices(self, point: Point) -> None:
if not isinstance(point, Point):
raise ValueError(f'Sea indices must be Points, not {type(point).__name__}')
if not((1 <= point.x <= self.WIDTH) and (1 <= point.y <= self.WIDTH)):
raise IndexError('Sea index out of range')
# Allows us to get the value using a point as a key, for example, `sea[Point(3,2)]`
def __getitem__(self, point: Point) -> int:
self._validate_item_indices(point)
return self._graph[point.y - 1][point.x -1]
# Allows us to get the value using a point as a key, for example, `sea[Point(3,2)] = 3`
def __setitem__(self, point: Point, value: int) -> None:
self._validate_item_indices(point)
self._graph[point.y - 1][point.x -1] = value
# Allows us to check if a point exists in the sea for example, `if Point(3,2) in sea:`
def __contains__(self, point: Point) -> bool:
try:
self._validate_item_indices(point)
except IndexError:
return False
return True
# Redefines how python will render this object when asked as a str
def __str__(self):
# Display it encoded
return "\n".join([' '.join([str(self._graph[y][x])
for y in range(self.WIDTH - 1, -1, -1)])
for x in range(self.WIDTH)])
def has_ship(self, ship_code: int) -> bool:
return any(ship_code in row for row in self._graph)
def count_sunk(self, *ship_codes: int) -> int:
return sum(not self.has_ship(ship_code) for ship_code in ship_codes)
class Battle:
def __init__(self) -> None:
self.sea = Sea()
self.place_ship(DESTROYER_LENGTH, 1)
self.place_ship(DESTROYER_LENGTH, 2)
self.place_ship(CRUISER_LENGTH, 3)
self.place_ship(CRUISER_LENGTH, 4)
self.place_ship(AIRCRAFT_CARRIER_LENGTH, 5)
self.place_ship(AIRCRAFT_CARRIER_LENGTH, 6)
self.splashes = 0
self.hits = 0
def _next_target(self) -> Point:
while True:
try:
guess = input('? ')
coordinates = guess.split(',')
if len(coordinates) != 2:
raise ValueError()
point = Point(int(coordinates[0]), int(coordinates[1]))
if point not in self.sea:
raise ValueError()
return point
except ValueError:
print(f'INVALID. SPECIFY TWO NUMBERS FROM 1 TO {Sea.WIDTH}, SEPARATED BY A COMMA.')
@property
def splash_hit_ratio(self) -> str:
return f'{self.splashes}/{self.hits}'
@property
def _is_finished(self) -> bool:
return self.sea.count_sunk(*(i for i in range(1, 7))) == 6
def place_ship(self, size: int, ship_code: int) -> None:
while True:
start = Point.random(1, self.sea.WIDTH + 1)
vector = Vector.random()
# Get potential ship points
points = [start + vector * i for i in range(size)]
if not (all([point in self.sea for point in points]) and
not any([self.sea[point] for point in points])):
# ship out of bounds or crosses other ship, trying again
continue
# We found a valid spot, so actually place it now
for point in points:
self.sea[point] = ship_code
break
def loop(self):
while True:
target = self._next_target()
target_value = self.sea[target]
if target_value < 0:
print(f'YOU ALREADY PUT A HOLE IN SHIP NUMBER {abs(target_value)} AT THAT POINT.')
if target_value <= 0:
print('SPLASH! TRY AGAIN.')
self.splashes += 1
continue
print(f'A DIRECT HIT ON SHIP NUMBER {target_value}')
self.hits += 1
self.sea[target] = -target_value
if not self.sea.has_ship(target_value):
print('AND YOU SUNK IT. HURRAH FOR THE GOOD GUYS.')
self._display_sunk_report()
if self._is_finished:
self._display_game_end()
break
print(f'YOUR CURRENT SPLASH/HIT RATIO IS {self.splash_hit_ratio}')
def _display_sunk_report(self):
print('SO FAR, THE BAD GUYS HAVE LOST',
f'{self.sea.count_sunk(1, 2)} DESTROYER(S),',
f'{self.sea.count_sunk(3, 4)} CRUISER(S),',
f'AND {self.sea.count_sunk(5, 6)} AIRCRAFT CARRIER(S).')
def _display_game_end(self):
print('YOU HAVE TOTALLY WIPED OUT THE BAD GUYS\' FLEET '
f'WITH A FINAL SPLASH/HIT RATIO OF {self.splash_hit_ratio}')
if not self.splashes:
print('CONGRATULATIONS -- A DIRECT HIT EVERY TIME.')
print("\n****************************")
def main() -> None:
game = Battle()
print(f'''
BATTLE
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
THE FOLLOWING CODE OF THE BAD GUYS' FLEET DISPOSITION
HAS BEEN CAPTURED BUT NOT DECODED:
{game.sea}
DE-CODE IT AND USE IT IF YOU CAN
BUT KEEP THE DE-CODING METHOD A SECRET.
START GAME''')
game.loop()
if __name__ == "__main__":
main()
+22
View File
@@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Battle", "Battle.vbproj", "{D8475464-CB9B-448F-89A7-5BA15193C495}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D8475464-CB9B-448F-89A7-5BA15193C495}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D8475464-CB9B-448F-89A7-5BA15193C495}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D8475464-CB9B-448F-89A7-5BA15193C495}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D8475464-CB9B-448F-89A7-5BA15193C495}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
+8
View File
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<RootNamespace>Battle</RootNamespace>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>16.9</LangVersion>
</PropertyGroup>
</Project>
+22
View File
@@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blackjack", "Blackjack.csproj", "{83253F48-9CCD-475C-A990-8703F1A2E31C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{83253F48-9CCD-475C-A990-8703F1A2E31C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{83253F48-9CCD-475C-A990-8703F1A2E31C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{83253F48-9CCD-475C-A990-8703F1A2E31C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{83253F48-9CCD-475C-A990-8703F1A2E31C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
+22
View File
@@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Blackjack", "Blackjack.vbproj", "{B112CA5F-142B-46E9-92CB-5E3A84816AAE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B112CA5F-142B-46E9-92CB-5E3A84816AAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B112CA5F-142B-46E9-92CB-5E3A84816AAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B112CA5F-142B-46E9-92CB-5E3A84816AAE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B112CA5F-142B-46E9-92CB-5E3A84816AAE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
+8
View File
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<RootNamespace>Blackjack</RootNamespace>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>16.9</LangVersion>
</PropertyGroup>
</Project>
+22
View File
@@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bombardment", "Bombardment.csproj", "{1DCFD283-9300-405B-A2B4-231F30265730}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1DCFD283-9300-405B-A2B4-231F30265730}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1DCFD283-9300-405B-A2B4-231F30265730}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1DCFD283-9300-405B-A2B4-231F30265730}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1DCFD283-9300-405B-A2B4-231F30265730}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
+22
View File
@@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Bombardment", "Bombardment.vbproj", "{7C967BC0-101C-413F-92A8-3A8A7D9846FE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7C967BC0-101C-413F-92A8-3A8A7D9846FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7C967BC0-101C-413F-92A8-3A8A7D9846FE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C967BC0-101C-413F-92A8-3A8A7D9846FE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C967BC0-101C-413F-92A8-3A8A7D9846FE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
+8
View File
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<RootNamespace>Bombardment</RootNamespace>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>16.9</LangVersion>
</PropertyGroup>
</Project>
+189
View File
@@ -0,0 +1,189 @@
"""
Bombs away
Ported from BASIC to Python3 by Bernard Cooke (bernardcooke53)
Tested with Python 3.8.10, formatted with Black and type checked with mypy.
"""
import random
from typing import Iterable
def _stdin_choice(prompt: str, *, choices: Iterable[str]) -> str:
ret = input(prompt)
while ret not in choices:
print("TRY AGAIN...")
ret = input(prompt)
return ret
def player_survived() -> None:
print("YOU MADE IT THROUGH TREMENDOUS FLAK!!")
def player_death() -> None:
print("* * * * BOOM * * * *")
print("YOU HAVE BEEN SHOT DOWN.....")
print("DEARLY BELOVED, WE ARE GATHERED HERE TODAY TO PAY OUR")
print("LAST TRIBUTE...")
def mission_success() -> None:
print(f"DIRECT HIT!!!! {int(100 * random.random())} KILLED.")
print("MISSION SUCCESSFUL.")
def death_with_chance(p_death: float) -> bool:
"""
Takes a float between 0 and 1 and returns a boolean
if the player has survived (based on random chance)
Returns True if death, False if survived
"""
return p_death > random.random()
def commence_non_kamikazi_attack() -> None:
while True:
try:
nmissions = int(input("HOW MANY MISSIONS HAVE YOU FLOWN? "))
while nmissions >= 160:
print("MISSIONS, NOT MILES...")
print("150 MISSIONS IS HIGH EVEN FOR OLD-TIMERS")
nmissions = int(input("NOW THEN, HOW MANY MISSIONS HAVE YOU FLOWN? "))
break
except ValueError:
# In the BASIC implementation this
# wasn't accounted for
print("TRY AGAIN...")
continue
if nmissions >= 100:
print("THAT'S PUSHING THE ODDS!")
if nmissions < 25:
print("FRESH OUT OF TRAINING, EH?")
print()
return (
mission_success() if nmissions >= 160 * random.random() else mission_failure()
)
def mission_failure() -> None:
weapons_choices = {
"1": "GUNS",
"2": "MISSILES",
"3": "BOTH",
}
print(f"MISSED TARGET BY {int(2 + 30 * random.random())} MILES!")
print("NOW YOU'RE REALLY IN FOR IT !!")
print()
enemy_weapons = _stdin_choice(
prompt="DOES THE ENEMY HAVE GUNS(1), MISSILES(2), OR BOTH(3)? ",
choices=weapons_choices,
)
# If there are no gunners (i.e. weapon choice 2) then
# we say that the gunners have 0 accuracy for the purposes
# of calculating probability of player death
enemy_gunner_accuracy = 0.0
if enemy_weapons != "2":
# If the enemy has guns, how accurate are the gunners?
while True:
try:
enemy_gunner_accuracy = float(
input("WHAT'S THE PERCENT HIT RATE OF ENEMY GUNNERS (10 TO 50)? ")
)
break
except ValueError:
# In the BASIC implementation this
# wasn't accounted for
print("TRY AGAIN...")
continue
if enemy_gunner_accuracy < 10:
print("YOU LIE, BUT YOU'LL PAY...")
return player_death()
missile_threat_weighting = 0 if enemy_weapons == "1" else 35
death = death_with_chance(
p_death=(enemy_gunner_accuracy + missile_threat_weighting) / 100
)
return player_survived() if not death else player_death()
def play_italy() -> None:
targets_to_messages = {
# 1 - ALBANIA, 2 - GREECE, 3 - NORTH AFRICA
"1": "SHOULD BE EASY -- YOU'RE FLYING A NAZI-MADE PLANE.",
"2": "BE CAREFUL!!!",
"3": "YOU'RE GOING FOR THE OIL, EH?",
}
target = _stdin_choice(
prompt="YOUR TARGET -- ALBANIA(1), GREECE(2), NORTH AFRICA(3)",
choices=targets_to_messages,
)
print(targets_to_messages[target])
return commence_non_kamikazi_attack()
def play_allies() -> None:
aircraft_to_message = {
"1": "YOU'VE GOT 2 TONS OF BOMBS FLYING FOR PLOESTI.",
"2": "YOU'RE DUMPING THE A-BOMB ON HIROSHIMA.",
"3": "YOU'RE CHASING THE BISMARK IN THE NORTH SEA.",
"4": "YOU'RE BUSTING A GERMAN HEAVY WATER PLANT IN THE RUHR.",
}
aircraft = _stdin_choice(
prompt="AIRCRAFT -- LIBERATOR(1), B-29(2), B-17(3), LANCASTER(4): ",
choices=aircraft_to_message,
)
print(aircraft_to_message[aircraft])
return commence_non_kamikazi_attack()
def play_japan() -> None:
print("YOU'RE FLYING A KAMIKAZE MISSION OVER THE USS LEXINGTON.")
first_mission = input("YOUR FIRST KAMIKAZE MISSION? (Y OR N): ")
if first_mission.lower() == "n":
return player_death()
return mission_success() if random.random() > 0.65 else player_death()
def play_germany() -> None:
targets_to_messages = {
# 1 - RUSSIA, 2 - ENGLAND, 3 - FRANCE
"1": "YOU'RE NEARING STALINGRAD.",
"2": "NEARING LONDON. BE CAREFUL, THEY'VE GOT RADAR.",
"3": "NEARING VERSAILLES. DUCK SOUP. THEY'RE NEARLY DEFENSELESS.",
}
target = _stdin_choice(
prompt="A NAZI, EH? OH WELL. ARE YOU GOING FOR RUSSIA(1),\nENGLAND(2), OR FRANCE(3)? ",
choices=targets_to_messages,
)
print(targets_to_messages[target])
return commence_non_kamikazi_attack()
def play_game() -> None:
print("YOU ARE A PILOT IN A WORLD WAR II BOMBER.")
sides = {"1": play_italy, "2": play_allies, "3": play_japan, "4": play_germany}
side = _stdin_choice(
prompt="WHAT SIDE -- ITALY(1), ALLIES(2), JAPAN(3), GERMANY(4): ", choices=sides
)
return sides[side]()
if __name__ == "__main__":
again = True
while again:
play_game()
again = True if input("ANOTHER MISSION? (Y OR N): ").upper() == "Y" else False
+22
View File
@@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "BombsAway", "BombsAway.vbproj", "{7FB28848-EC1C-4594-B823-BA6DB06B34A8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7FB28848-EC1C-4594-B823-BA6DB06B34A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7FB28848-EC1C-4594-B823-BA6DB06B34A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7FB28848-EC1C-4594-B823-BA6DB06B34A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7FB28848-EC1C-4594-B823-BA6DB06B34A8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
+8
View File
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<RootNamespace>BombsAway</RootNamespace>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>16.9</LangVersion>
</PropertyGroup>
</Project>
+9
View File
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>10</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
+22
View File
@@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bounce", "Bounce.csproj", "{4A967985-8CB0-49D2-B322-B2668491CA6E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4A967985-8CB0-49D2-B322-B2668491CA6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4A967985-8CB0-49D2-B322-B2668491CA6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4A967985-8CB0-49D2-B322-B2668491CA6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4A967985-8CB0-49D2-B322-B2668491CA6E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
+22
View File
@@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Bounce", "Bounce.vbproj", "{95D84C53-AE4E-4A5A-869A-A49D6FC89618}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{95D84C53-AE4E-4A5A-869A-A49D6FC89618}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{95D84C53-AE4E-4A5A-869A-A49D6FC89618}.Debug|Any CPU.Build.0 = Debug|Any CPU
{95D84C53-AE4E-4A5A-869A-A49D6FC89618}.Release|Any CPU.ActiveCfg = Release|Any CPU
{95D84C53-AE4E-4A5A-869A-A49D6FC89618}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
+8
View File
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<RootNamespace>Bounce</RootNamespace>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>16.9</LangVersion>
</PropertyGroup>
</Project>
+198
View File
@@ -0,0 +1,198 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bowling
{
public class Bowling
{
private readonly Pins pins = new();
private int players;
public void Play()
{
ShowBanner();
MaybeShowInstructions();
Setup();
GameLoop();
}
private static void ShowBanner()
{
Utility.PrintString(34, "BOWL");
Utility.PrintString(15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
Utility.PrintString();
Utility.PrintString();
Utility.PrintString();
Utility.PrintString("WELCOME TO THE ALLEY");
Utility.PrintString("BRING YOUR FRIENDS");
Utility.PrintString("OKAY LET'S FIRST GET ACQUAINTED");
Utility.PrintString();
}
private static void MaybeShowInstructions()
{
Utility.PrintString("THE INSTRUCTIONS (Y/N)");
if (Utility.InputString() == "N") return;
Utility.PrintString("THE GAME OF BOWLING TAKES MIND AND SKILL.DURING THE GAME");
Utility.PrintString("THE COMPUTER WILL KEEP SCORE.YOU MAY COMPETE WITH");
Utility.PrintString("OTHER PLAYERS[UP TO FOUR].YOU WILL BE PLAYING TEN FRAMES");
Utility.PrintString("ON THE PIN DIAGRAM 'O' MEANS THE PIN IS DOWN...'+' MEANS THE");
Utility.PrintString("PIN IS STANDING.AFTER THE GAME THE COMPUTER WILL SHOW YOUR");
Utility.PrintString("SCORES .");
}
private void Setup()
{
Utility.PrintString("FIRST OF ALL...HOW MANY ARE PLAYING", false);
var input = Utility.InputInt();
players = input < 1 ? 1 : input;
Utility.PrintString();
Utility.PrintString("VERY GOOD...");
}
private void GameLoop()
{
GameResults[] gameResults = InitGameResults();
var done = false;
while (!done)
{
ResetGameResults(gameResults);
for (int frame = 0; frame < GameResults.FramesPerGame; ++frame)
{
for (int player = 0; player < players; ++player)
{
pins.Reset();
int pinsDownThisFrame = pins.GetPinsDown();
int ball = 1;
while (ball == 1 || ball == 2) // One or two rolls
{
Utility.PrintString("TYPE ROLL TO GET THE BALL GOING.");
_ = Utility.InputString();
int pinsDownAfterRoll = pins.Roll();
ShowPins(player, frame, ball);
if (pinsDownAfterRoll == pinsDownThisFrame)
{
Utility.PrintString("GUTTER!!");
}
if (ball == 1)
{
// Store current pin count
gameResults[player].Results[frame].PinsBall1 = pinsDownAfterRoll;
// Special handling for strike
if (pinsDownAfterRoll == Pins.TotalPinCount)
{
Utility.PrintString("STRIKE!!!!!\a\a\a\a");
// No second roll
ball = 0;
gameResults[player].Results[frame].PinsBall2 = pinsDownAfterRoll;
gameResults[player].Results[frame].Score = FrameResult.Points.Strike;
}
else
{
ball = 2; // Roll again
Utility.PrintString("ROLL YOUR SECOND BALL");
}
}
else if (ball == 2)
{
// Store current pin count
gameResults[player].Results[frame].PinsBall2 = pinsDownAfterRoll;
ball = 0;
// Determine the score for the frame
if (pinsDownAfterRoll == Pins.TotalPinCount)
{
Utility.PrintString("SPARE!!!!");
gameResults[player].Results[frame].Score = FrameResult.Points.Spare;
}
else
{
Utility.PrintString("ERROR!!!");
gameResults[player].Results[frame].Score = FrameResult.Points.Error;
}
}
Utility.PrintString();
}
}
}
ShowGameResults(gameResults);
Utility.PrintString("DO YOU WANT ANOTHER GAME");
var a = Utility.InputString();
done = a.Length == 0 || a[0] != 'Y';
}
}
private GameResults[] InitGameResults()
{
var gameResults = new GameResults[players];
for (int i = 0; i < gameResults.Length; i++)
{
gameResults[i] = new GameResults();
}
return gameResults;
}
private void ShowPins(int player, int frame, int ball)
{
Utility.PrintString($"FRAME: {frame + 1} PLAYER: {player + 1} BALL: {ball}");
var breakPins = new bool[] { true, false, false, false, true, false, false, true, false, true };
var indent = 0;
for (int pin = 0; pin < Pins.TotalPinCount; ++pin)
{
if (breakPins[pin])
{
Utility.PrintString(); // End row
Utility.PrintString(indent++, false); // Indent next row
}
var s = pins[pin] == Pins.State.Down ? "+ " : "o ";
Utility.PrintString(s, false);
}
Utility.PrintString();
Utility.PrintString();
}
private void ResetGameResults(GameResults[] gameResults)
{
foreach (var gameResult in gameResults)
{
foreach (var frameResult in gameResult.Results)
{
frameResult.Reset();
}
}
}
private void ShowGameResults(GameResults[] gameResults)
{
Utility.PrintString("FRAMES");
for (int i = 0; i < GameResults.FramesPerGame; ++i)
{
Utility.PrintString(Utility.PadInt(i, 3), false);
}
Utility.PrintString();
foreach (var gameResult in gameResults)
{
foreach (var frameResult in gameResult.Results)
{
Utility.PrintString(Utility.PadInt(frameResult.PinsBall1, 3), false);
}
Utility.PrintString();
foreach (var frameResult in gameResult.Results)
{
Utility.PrintString(Utility.PadInt(frameResult.PinsBall2, 3), false);
}
Utility.PrintString();
foreach (var frameResult in gameResult.Results)
{
Utility.PrintString(Utility.PadInt((int)frameResult.Score, 3), false);
}
Utility.PrintString();
Utility.PrintString();
}
}
}
}
+9
View File
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>10</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
+22
View File
@@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bowling", "Bowling.csproj", "{9951637A-8D70-42A4-8CB7-315FA414F960}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9951637A-8D70-42A4-8CB7-315FA414F960}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9951637A-8D70-42A4-8CB7-315FA414F960}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9951637A-8D70-42A4-8CB7-315FA414F960}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9951637A-8D70-42A4-8CB7-315FA414F960}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
+23
View File
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bowling
{
public class FrameResult
{
public enum Points { None, Error, Spare, Strike };
public int PinsBall1 { get; set; }
public int PinsBall2 { get; set; }
public Points Score { get; set; }
public void Reset()
{
PinsBall1 = PinsBall2 = 0;
Score = Points.None;
}
}
}
+23
View File
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bowling
{
public class GameResults
{
public static readonly int FramesPerGame = 10;
public FrameResult[] Results { get; set; }
public GameResults()
{
Results = new FrameResult[FramesPerGame];
for (int i = 0; i < FramesPerGame; ++i)
{
Results[i] = new FrameResult();
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More