Merge remote-tracking branch 'origin/main' into main

This commit is contained in:
Paul Holt
2022-01-16 01:48:57 +11:00
64 changed files with 8295 additions and 221 deletions
+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,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
@@ -0,0 +1,27 @@
using System.Diagnostics.CodeAnalysis;
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 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; }
var path1 = path.TrimEnd('\\');
rootPath = rootPath.TrimEnd('\\');
if (!path1.StartsWith(rootPath, StringComparison.InvariantCultureIgnoreCase)) { return path; }
return path1[(rootPath.Length + 1)..]; // ignore the initial /
}
}
@@ -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,51 @@
using System.Reflection;
using static System.IO.Directory;
using static System.IO.Path;
using static DotnetUtils.Globals;
namespace DotnetUtils;
public record PortInfo(
string FullPath, 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
};
public static PortInfo? Create(string fullPath, string langKeyword) {
var folderName = GetFileName(fullPath);
var parts = folderName.Split('_', 2);
var index =
parts.Length > 0 && int.TryParse(parts[0], out var n) ?
n :
(int?)null;
var gameName =
parts.Length > 1 ?
parts[1].Replace("_", "") :
null;
if (index is 0 or null || gameName is null) { return null; }
var (ext, projExt) = LangData[langKeyword];
var langPath = Combine(fullPath, langKeyword);
var codeFiles =
GetFiles(langPath, $"*.{ext}", enumerationOptions)
.Where(x => !x.Contains("\\bin\\") && !x.Contains("\\obj\\"))
.ToArray();
return new PortInfo(
fullPath, 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(fullPath => LangData.Keys.Select(keyword => (fullPath, keyword)))
.SelectT((fullPath, keyword) => PortInfo.Create(fullPath, keyword))
.Where(x => x is not null)
.ToArray()!;
}
public static readonly PortInfo[] Get;
}
@@ -0,0 +1,166 @@
using DotnetUtils;
using static System.Console;
using static System.IO.Path;
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")
};
foreach (var (_, description, index) in actions.WithIndex()) {
WriteLine($"{index}: {description}");
}
WriteLine();
actions[getChoice(actions.Length - 1)].action();
int getChoice(int maxValue) {
int result;
do {
Write("? ");
} while (!int.TryParse(ReadLine(), out result) || result < 0 || result > maxValue);
WriteLine();
return result;
}
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;
}
WriteLine();
}
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.Any()).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.Any()) { continue; }
var expectedSlnName = $"{item.GameName}.sln";
if (item.Slns.Contains(Combine(item.LangPath, expectedSlnName))) { 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.Any()).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.Any()) { 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}");
}
+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>
@@ -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;
+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.
+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();
}
}
+1 -1
View File
@@ -13,7 +13,7 @@ class Basketball():
self.defense = None
self.defense_choices = [6, 6.5, 7, 7.5]
self.shot = None
self.shot_choices = [1, 2, 3, 4]
self.shot_choices = [0, 1, 2, 3, 4]
self.z1 = None
# Explains the keyboard inputs
+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;
}
}
+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
+10 -34
View File
@@ -1,41 +1,17 @@
import java.util.Scanner;
import static java.lang.System.out;
// This is very close to the original BASIC. Changes:
// 1) the array indexing is adjusted by 1
// 2) the user can enter a lower case "y"
// 3) moved the word list to the top 8~)
public class Buzzword {
private static final String[] A = {
"ABILITY","BASAL","BEHAVIORAL","CHILD-CENTERED",
"DIFFERENTIATED","DISCOVERY","FLEXIBLE","HETEROGENEOUS",
"HOMOGENEOUS","MANIPULATIVE","MODULAR","TAVISTOCK",
"INDIVIDUALIZED","LEARNING","EVALUATIVE","OBJECTIVE",
"COGNITIVE","ENRICHMENT","SCHEDULING","HUMANISTIC",
"INTEGRATED","NON-GRADED","TRAINING","VERTICAL AGE",
"MOTIVATIONAL","CREATIVE","GROUPING","MODIFICATION",
"ACCOUNTABILITY","PROCESS","CORE CURRICULUM","ALGORITHM",
"PERFORMANCE","REINFORCEMENT","OPEN CLASSROOM","RESOURCE",
"STRUCTURE","FACILITY","ENVIRONMENT"
};
private static Scanner scanner = new Scanner( System.in );
public static void main( final String [] args ) {
out.println( " BUZZWORD GENERATOR" );
out.println( " CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" );
out.println();out.println();out.println();
out.println( "THIS PROGRAM PRINTS HIGHLY ACCEPTABLE PHRASES IN" );
out.println( "'EDUCATOR-SPEAK' THAT YOU CAN WORK INTO REPORTS" );
out.println( "AND SPEECHES. WHENEVER A QUESTION MARK IS PRINTED," );
out.println( "TYPE A 'Y' FOR ANOTHER PHRASE OR 'N' TO QUIT." );
out.println();out.println();out.println( "HERE'S THE FIRST PHRASE:" );
do {
out.print( A[ (int)( 13 * Math.random() ) ] + " " );
out.print( A[ (int)( 13 * Math.random() + 13 ) ] + " " );
out.print( A[ (int)( 13 * Math.random() + 26 ) ] ); out.println();
out.print( "?" );
public static void main(final String[] args) {
try (
// Scanner is a Closeable so it must be closed
// before the program ends.
final Scanner scanner = new Scanner(System.in);
) {
final BuzzwordSupplier buzzwords = new BuzzwordSupplier();
final UserInterface userInterface = new UserInterface(
scanner, System.out, buzzwords);
userInterface.run();
}
while ( "Y".equals( scanner.nextLine().toUpperCase() ) );
out.println( "COME BACK WHEN YOU NEED HELP WITH ANOTHER REPORT!" );
}
}
@@ -0,0 +1,39 @@
import java.util.Random;
import java.util.function.Supplier;
/**
* A string supplier that provides an endless stream of random buzzwords.
*/
public class BuzzwordSupplier implements Supplier<String> {
private static final String[] SET_1 = {
"ABILITY","BASAL","BEHAVIORAL","CHILD-CENTERED",
"DIFFERENTIATED","DISCOVERY","FLEXIBLE","HETEROGENEOUS",
"HOMOGENEOUS","MANIPULATIVE","MODULAR","TAVISTOCK",
"INDIVIDUALIZED" };
private static final String[] SET_2 = {
"LEARNING","EVALUATIVE","OBJECTIVE",
"COGNITIVE","ENRICHMENT","SCHEDULING","HUMANISTIC",
"INTEGRATED","NON-GRADED","TRAINING","VERTICAL AGE",
"MOTIVATIONAL","CREATIVE" };
private static final String[] SET_3 = {
"GROUPING","MODIFICATION", "ACCOUNTABILITY","PROCESS",
"CORE CURRICULUM","ALGORITHM", "PERFORMANCE",
"REINFORCEMENT","OPEN CLASSROOM","RESOURCE", "STRUCTURE",
"FACILITY","ENVIRONMENT" };
private final Random random = new Random();
/**
* Create a buzzword by concatenating a random word from each of the
* three word sets.
*/
@Override
public String get() {
return SET_1[random.nextInt(SET_1.length)] + ' ' +
SET_2[random.nextInt(SET_2.length)] + ' ' +
SET_3[random.nextInt(SET_3.length)];
}
}
+64
View File
@@ -0,0 +1,64 @@
import java.io.PrintStream;
import java.util.Scanner;
import java.util.function.Supplier;
/**
* A command line user interface that outputs a buzzword every
* time the user requests a new one.
*/
public class UserInterface implements Runnable {
/**
* Input from the user.
*/
private final Scanner input;
/**
* Output to the user.
*/
private final PrintStream output;
/**
* The buzzword generator.
*/
private final Supplier<String> buzzwords;
/**
* Create a new user interface.
*
* @param input The input scanner with which the user gives commands.
* @param output The output to show messages to the user.
* @param buzzwords The buzzword supplier.
*/
public UserInterface(final Scanner input,
final PrintStream output,
final Supplier<String> buzzwords) {
this.input = input;
this.output = output;
this.buzzwords = buzzwords;
}
@Override
public void run() {
output.println(" BUZZWORD GENERATOR");
output.println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
output.println();
output.println();
output.println();
output.println("THIS PROGRAM PRINTS HIGHLY ACCEPTABLE PHRASES IN");
output.println("'EDUCATOR-SPEAK' THAT YOU CAN WORK INTO REPORTS");
output.println("AND SPEECHES. WHENEVER A QUESTION MARK IS PRINTED,");
output.println("TYPE A 'Y' FOR ANOTHER PHRASE OR 'N' TO QUIT.");
output.println();
output.println();
output.println("HERE'S THE FIRST PHRASE:");
do {
output.println(buzzwords.get());
output.println();
output.print("?");
} while ("Y".equals(input.nextLine().toUpperCase()));
output.println("COME BACK WHEN YOU NEED HELP WITH ANOTHER REPORT!");
}
}
+10
View File
@@ -1,3 +1,13 @@
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
Conversion to [Perl](https://www.perl.org/)
Actually, this is not so much a port as a complete rewrite, making use of
Perl's Posix time functionality. The calendar is for the current year (not
1979), but you can get another year by specifying it on the command line, e.g.
`perl 21_Calendar/perl/calendar.pl 2001`
It *may* even produce output in languages other than English. But the
leftmost column will still be Sunday, even in locales where it is
typically Monday.
+130
View File
@@ -0,0 +1,130 @@
#!/usr/bin/env perl
use 5.010; # To get 'state' and 'say'
use strict; # Require explicit declaration of variables
use warnings; # Enable optional compiler warnings
use English; # Use more friendly names for Perl's magic variables
use POSIX qw{ strftime };
use Term::ReadLine; # Prompt and return user input
use Time::Local ();
BEGIN {
*time_gm =
Time::Local->can( 'timegm_modern' ) ||
Time::Local->can( 'timegm' );
}
our $VERSION = '0.000_01';
use constant COLUMN_WIDTH => 6;
use constant SECONDS_PER_DAY => 86400;
binmode STDOUT, ':encoding(utf-8)';
my $year = @ARGV ? $ARGV[0] : ( localtime )[5] + 1900;
my $is_leap_year = is_leap_year( $year );
my $year_len = 365 + $is_leap_year;
print <<'EOD';
CALENDAR
Creative Computing Morristown, New Jersey
EOD
my @mon_len = ( 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
$mon_len[1] += $is_leap_year;
foreach my $month ( 0 .. 11 ) {
my $epoch = time_gm( 0, 0, 0, 1, $month, $year );
my @start_time = gmtime( $epoch );
my ( $week_day, $year_day ) = @start_time[ 6, 7 ];
my $label = strftime( '%B %Y', @start_time );
$label .= ' ' x ( ( 14 - length $label ) / 2 );
printf "\n** %3d ****** %14s ****** %3d **\n",
$year_day, $label, $year_len - $year_day;
{
my $day = 1 + ( 7 - $week_day ) % 7;
foreach my $wd ( 0 .. 6 ) {
my $ep = time_gm( 0, 0, 0, $day + $wd, $month, $year );
printf '%*s', COLUMN_WIDTH, strftime( '%a', gmtime $ep );
}
print "\n";
}
say '*' x ( COLUMN_WIDTH * 7 );
print ' ' x ( COLUMN_WIDTH * $week_day );
my $month_day = 1;
while ( $week_day < 7 ) {
printf '%*d', COLUMN_WIDTH, $month_day++;
$week_day++;
}
print "\n";
$week_day = 0;
while ( $month_day <= $mon_len[$month] ) {
printf '%*d', COLUMN_WIDTH, $month_day++;
$week_day++;
unless ( $week_day % 7 ) {
print "\n";
$week_day = 0;
}
}
print "\n" if $week_day;
}
sub is_leap_year {
my ( $year ) = 1;
return 0 if $year % 4;
return 1 if $year % 100;
return 0 if $year % 400;
return 1;
}
__END__
=head1 TITLE
calendar - Play the game 'Calendar' from Basic Computer Games
=head1 SYNOPSIS
calendar.pl
=head1 DETAILS
This Perl script is a port of calendar, which is the 21st
entry in Basic Computer Games.
Actually, it is not so much a port as a complete rewrite, making use of
Perl's Posix time functionality. The calendar is for the current year
(not 1979), but you can get another year by specifying it on the command
line, e.g.
perl 21_Calendar/perl/calendar.pl 2001
It B<may> even produce output in languages other than English. But the
leftmost column will still be Sunday, even in locales where it is
typically Monday.
=head1 PORTED BY
Thomas R. Wyant, III F<wyant at cpan dot org>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2022 by Thomas R. Wyant, III
This program is free software; you can redistribute it and/or modify it
under the same terms as Perl 5.10.0. For more details, see the Artistic
License 1.0 at
L<https://www.perlfoundation.org/artistic-license-10.html>, and/or the
Gnu GPL at L<http://www.gnu.org/licenses/old-licenses/gpl-1.0.txt>.
This program is distributed in the hope that it will be useful, but
without any warranty; without even the implied warranty of
merchantability or fitness for a particular purpose.
=cut
# ex: set expandtab tabstop=4 textwidth=72 :
+98
View File
@@ -0,0 +1,98 @@
def tab(size)
str = ''
size.times do
str += ' '
end
str
end
def input
gets.chomp
end
def bye
print "BYE!!!\n"
end
def main
print tab(30), "CHIEF\n"
print tab(15), "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n"
print "\n"
print "\n"
print "\n"
print "I AM CHIEF NUMBERS FREEK, THE GREAT INDIAN MATH GOD.\n"
print "ARE YOU READY TO TAKE THE TEST YOU CALLED ME OUT FOR\n"
a = input
if a != 'YES'
print "SHUT UP, PALE FACE WITH WISE TONGUE.\n"
end
print " TAKE A NUMBER AND ADD 3. DIVIDE THIS NUMBER BY 5 AND\n"
print "MULTIPLY BY 8. DIVIDE BY 5 AND ADD THE SAME. SUBTRACT 1.\n"
print " WHAT DO YOU HAVE\n"
b = input.to_f
c = ((b + 1 - 5) * 5 / 8 * 5 -3).to_f
print "I BET YOUR NUMBER WAS #{c}. AM I RIGHT\n"
d = input
if d == 'YES'
return bye
end
print "WHAT WAS YOUR ORIGINAL NUMBER\n"
k = input.to_f
f = k + 3
g = f / 5
h = g * 8
i = h / 5 + 5
j = i - 1
print "SO YOU THINK YOU'RE SO SMART, EH?\n"
print "NOW WATCH.\n"
print k, " PLUS 3 EQUALS ", f, ". THIS DIVIDED BY 5 EQUALS ", g, ";\n"
print "THIS TIMES 8 EQUALS ", h, ". IF WE DIVIDE BY 5 AND ADD 5,\n"
print "WE GET ", i, ", WHICH, MINUS 1, EQUALS ", j, ".\n"
print "NOW DO YOU BELIEVE ME\n"
z = input
if z == 'YES'
return bye
end
print "YOU HAVE MADE ME MAD!!!\n"
print "THERE MUST BE A GREAT LIGHTNING BOLT!\n"
print "\n"
print "\n"
x = 30
while x >= 22
print tab(x), "X X\n"
x -= 1
end
print tab(21), "X XXX\n"
print tab(20), "X X\n"
print tab(19), "XX X\n"
y = 20
while y >= 13
print tab(y), "X X\n"
y -= 1
end
print tab(12), "XX\n"
print tab(11), "X\n"
print tab(10), "*\n"
print "\n"
print "#########################\n"
print "\n"
print "I HOPE YOU BELIEVE ME NOW, FOR YOUR SAKE!!\n"
return bye
end
main
+208
View File
@@ -17,3 +17,211 @@ As published in Basic Computer Games (1978):
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html
### Comments on the BASIC code for re-implementers.
15 LET R=0
`R` is a variable that tracks winnings and losings. Unlike other games that
start out with a lump sum of cash to spend this game assumes the user has as
much money as they want and we only track how much they lost or won.
21 LET T=1
22 PRINT "PICK A NUMBER AND INPUT TO ROLL DICE";
23 INPUT Z
24 LET X=(RND(0))
25 LET T =T+1
26 IF T<=Z THEN 24
This block of code does nothing other than try to scramble the random number
generator. Random number generation is not random, they are generated from the
previous generated number. Because of the slow speed of these systems back then,
gaming random number generators was a concern, mostly for gameplay quality.
If you could know the "seed value" to the generator then you could effectively
know how to get the exact same dice rolls to happen and change your bet to
maximize your winnings and minimize your losses.
The first reason this is an example of bad coding practice is the user is asked
to input a number but no clue is given as to the use of this number. This number
has no bearing on the game and as we'll see only has bearing on the internal
implementation of somehow trying to get an un-game-able seed for the random number
generator (since all future random numbers generated are based off this seed value.)
The `RND(1)` command generates a number from a seed value that is always
the same, everytime, from when the machine is booted up (old C64 behavior). In
order to avoid the same dice rolls being generated, a special call to `RND(-TI)`
would initialize the random generator with something else. But RND(-TI) is not
a valid command on all systems. So `RND(0)`, which generates a random number
from the system clock is used. But technically this could be gamed because the
system clock was driven by the bootup time, there wasn't a BIOS battery on these
systems that kept an internal real time clock going even when the system was
turned off, unlike your regular PC. Therefore, in order to ensure as true
randomness as possible, insert human reaction time by asking for human input.
But a human could just be holding down the enter key on bootup and that would
just skip any kind of multi-millisecond variance assigned by a natural human
reaction time. So, paranoia being a great motivator, a number is asked of the
user to avoid just holding down the enter key which negates the timing variance
of a human reaction.
What comes next is a bit of nonsense. The block of code loops a counter, recalling
the `RND(0)` function (and thus reseeding it with the system clock value)
and then comparing the counter to the user's number input
in order to bail out of the loop. Because the `RND(0)` function is based off the
system clock and the loop of code has no branching other than the bailout
condition, the loop also takes a fixed amount of time to execute, thus making
repeated calls to `RND(0)` predictive and this scheming to get a better random
number is pointless. Furthermore, the loop is based on the number the user inputs
so a huge number like ten million causes a very noticable delay and leaves the
user wondering if the program has errored. The author could have simply called
`RND(0)` once and used a prompt that made more sense like asking for the users
name and then using that name in the game's replies.
It is advised that you use whatever your languages' random number generator
provides and simply skip trying to recreate this bit of nonsense including
the user input.
27 PRINT"INPUT THE AMOUNT OF YOUR WAGER.";
28 INPUT F
30 PRINT "I WILL NOW THROW THE DICE"
40 LET E=INT(7*RND(1))
41 LET S=INT(7*RND(1))
42 LET X=E+S
.... a bit later ....
60 IF X=1 THEN 40
65 IF X=0 THEN 40
`F` is a variable that represents the users wager for this betting round.
`E` and `S` represent the two individual and random dice being rolled.
This code is actually wrong because it returns a value between 0 and 6.
`X` is the sum of these dice rolls. As you'll see though further down in the
code, if `X` is zero or one it re-rolls the dice to maintain a potential
outcome of the sum of two dice between 2 and 12. This skews the normal distribution
of dice values to favor lower numbers because it does not consider that `E`
could be zero and `S` could be 2 or higher. To show this skewing of values
you can run the `distribution.bas` program which creates a histogram of the
distribution of the bad dice throw code and proper dice throw code.
Here are the results:
DISTRIBUTION OF DICE ROLLS WITH INT(7*RND(1)) VS INT(6*RND(1)+1)
THE INT(7*RND(1)) DISTRIBUTION:
2 3 4 5 6 7 8 9 10 11 12
6483 8662 10772 13232 15254 13007 10746 8878 6486 4357 2123
THE INT(6*RND(1)+1) DISTRIBUTION
2 3 4 5 6 7 8 9 10 11 12
2788 5466 8363 11072 13947 16656 13884 11149 8324 5561 2790
If the dice rolls are fair then we should see the largest occurrence be a 7 and
the smallest should be 2 and 12. Furthermore the occurrences should be
symetrical meaning there should be roughly the same amount of 2's as 12's, the
same amount of 3's as 11's, 4's as 10's and so on until you reach the middle, 7.
But notice in the skewed dice roll, 6 is the most rolled number not 7, and the
rest of the numbers are not symetrical, there are many more 2's than 12's.
So the lesson is test your code.
The proper way to model a dice throw, in almost every language is
`INT(6*RND(1)+1)` or `INT(6*RND(1))+1`
SideNote: `X` was used already in the
previous code block discussed but its value was never used. This is another
poor coding practice: **Don't reuse variable names for different purposes.**
50 IF X=7 THEN 180
55 IF X=11 THEN 180
60 IF X=1 THEN 40
62 IF X=2 THEN 195
65 IF X=0 THEN 40
70 IF X=2 THEN 200
80 IF X=3 THEN 200
90 IF X=12 THEN 200
125 IF X=5 THEN 220
130 IF X =6 THEN 220
140 IF X=8 THEN 220
150 IF X=9 THEN 220
160 IF X =10 THEN 220
170 IF X=4 THEN 220
This bit of code determines the routing of where to go for payout, or loss.
Of course, line 60 and 65 are pointless as we've just shown and should be removed
as long as the correct dice algorithm is also changed.
62 IF X=2 THEN 195
....
70 IF X=2 THEN 200
The check for a 2 has already been made and the jump is done. Line 70 is
therefore redundant and can be left out. The purpose of line 62 is only to
print a special output, "SNAKE EYES!" which we'll see in the next block creates
duplicate code.
Lines 125-170 are also pointlessly checked because we know previous values have
been ruled out, only these last values must remain, and they are all going to
the same place, line 220. Line 125-170 could have simply been replaced with
`GOTO 220`
180 PRINT X "- NATURAL....A WINNER!!!!"
185 PRINT X"PAYS EVEN MONEY, YOU WIN"F"DOLLARS"
190 GOTO 210
195 PRINT X"- SNAKE EYES....YOU LOSE."
196 PRINT "YOU LOSE"F "DOLLARS."
197 LET F=0-F
198 GOTO 210
200 PRINT X " - CRAPS...YOU LOSE."
205 PRINT "YOU LOSE"F"DOLLARS."
206 LET F=0-F
210 LET R= R+F
211 GOTO 320
This bit of code manages instant wins or losses due to 7,11 or 2,3,12. As
mentioned previously, lines 196 and 197 are essentially the same as lines
205 and 206. A simpler code would be just to jump after printing the special
message of "SNAKE EYES!" to line 205.
Lines 197 and 206 just negate the wager by subtracting it from zero. Just saying
`F = -F` would have sufficed. Line 210 updates your running total of winnings
or losses with this bet.
220 PRINT X "IS THE POINT. I WILL ROLL AGAIN"
230 LET H=INT(7*RND(1))
231 LET Q=INT(7*RND(1))
232 LET O=H+Q
240 IF O=1 THEN 230
250 IF O=7 THEN 290
255 IF O=0 THEN 230
This code sets the point, the number you must re-roll to win without rolling
a 7, the most probable number to roll. Except in this case again, it has the
same incorrect dice rolling code and therefore 6 is the most probable number
to roll. The concept of DRY (don't repeat yourself) is a coding practice which
encourages non-duplication of code because if there is an error in the code, it
can be fixed in one place and not multiple places like in this code. The scenario
might be that a programmer sees some wrong code, fixes it, but neglects to
consider that there might be duplicates of the same wrong code elsewhere. If
you practice DRY then you never worry much about behaviors in your code diverging
due to duplicate code snippets.
260 IF O=X THEN 310
270 PRINT O " - NO POINT. I WILL ROLL AGAIN"
280 GOTO 230
290 PRINT O "- CRAPS. YOU LOSE."
291 PRINT "YOU LOSE $"F
292 F=0-F
293 GOTO 210
300 GOTO 320
310 PRINT X"- A WINNER.........CONGRATS!!!!!!!!"
311 PRINT X "AT 2 TO 1 ODDS PAYS YOU...LET ME SEE..."2*F"DOLLARS"
312 LET F=2*F
313 GOTO 210
This is the code to keep rolling until the point is made or a seven is rolled.
Again we see the negated `F` wager and lose message duplicated. This code could
have been reorganized using a subroutine, or in BASIC, the GOSUB command, but
in your language its most likely just known as a function or method. You can
do a `grep -r 'GOSUB'` from the root directory to see other BASIC programs in
this set that use GOSUB.
The rest of the code if fairly straight forward, replay the game or end with
a report of your winnings or losings.
+24
View File
@@ -0,0 +1,24 @@
10 PRINT "DISTRIBUTION OF DICE ROLLS WITH INT(7*RND(1)) VS INT(6*RND(1)+1)"
20 DIM A(12)
30 DIM B(12)
100 FOR X = 1 TO 100000 : REM CHOOSE A LARGE NUMBER TO GET A FINER GRAINED HISTOGRAM
140 REM GET A NUMBER FROM 0 TO 6 INCLUSIVE WITH THE INTENT TO THROW AWAY ZEROES.
150 LET D1 = INT(7*RND(1))
155 LET D2 = INT(7*RND(1))
160 LET S1 = D1+D2
165 REM IF THIS SUM IS LESS THAN TWO THEN TRY AGAIN.
170 IF S1<2 THEN 150
199 REM GET A NUMBER FROM 0 TO 5 THEN ADD 1 TO IT TO MAKE IT 1 TO 6
200 LET D3 = INT(6*RND(1))+1
210 LET D4 = INT(6*RND(1))+1
220 LET S2 = D3+D4
245 REM USE OUR ARRAY AS A HISTOGRAM, COUNTING EACH OCCURRENCE OF DICE ROLL
250 A(S1) = A(S1) + 1
260 B(S2) = B(S2) + 1
290 NEXT X
300 PRINT "THE INT(7*RND(1)) DISTRIBUTION:"
310 FOR I = 2 TO 12 :PRINT I,:NEXT:PRINT
320 FOR I = 2 TO 12 :PRINT A(I),:NEXT:PRINT
325 PRINT "THE INT(6*RND(1)+1) DISTRIBUTION"
330 FOR I = 2 TO 12 :PRINT I,:NEXT:PRINT
340 FOR I = 2 TO 12 :PRINT B(I),:NEXT:PRINT
+125
View File
@@ -0,0 +1,125 @@
import java.util.Random;
import java.util.Scanner;
/**
* Port of Craps from BASIC to Java 17.
*/
public class Craps {
public static final Random random = new Random();
public static void main(String[] args) {
System.out.println("""
CRAPS
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
2,3,12 ARE LOSERS; 4,5,6,8,9,10 ARE POINTS; 7,11 ARE NATURAL WINNERS.
""");
double winnings = 0.0;
do {
winnings = playCraps(winnings);
} while (stillInterested(winnings));
winningsReport(winnings);
}
public static double playCraps(double winnings) {
double wager = getWager();
System.out.println("I WILL NOW THROW THE DICE");
int roll = rollDice();
double payout = switch (roll) {
case 7, 11 -> naturalWin(roll, wager);
case 2, 3, 12 -> lose(roll, wager);
default -> setPoint(roll, wager);
};
return winnings + payout;
}
public static int rollDice() {
return random.nextInt(1, 7) + random.nextInt(1, 7);
}
private static double setPoint(int point, double wager) {
System.out.printf("%1$ d IS THE POINT. I WILL ROLL AGAIN%n",point);
return makePoint(point, wager);
}
private static double makePoint(int point, double wager) {
int roll = rollDice();
if (roll == 7)
return lose(roll, wager);
if (roll == point)
return win(roll, wager);
System.out.printf("%1$ d - NO POINT. I WILL ROLL AGAIN%n", roll);
return makePoint(point, wager); // recursive
}
private static double win(int roll, double wager) {
double payout = 2 * wager;
System.out.printf("%1$ d - A WINNER.........CONGRATS!!!!!!!!%n", roll);
System.out.printf("%1$ d AT 2 TO 1 ODDS PAYS YOU...LET ME SEE...$%2$3.2f%n",
roll, payout);
return payout;
}
private static double lose(int roll, double wager) {
String msg = roll == 2 ? "SNAKE EYES.":"CRAPS";
System.out.printf("%1$ d - %2$s...YOU LOSE.%n", roll, msg);
System.out.printf("YOU LOSE $%3.2f%n", wager);
return -wager;
}
public static double naturalWin(int roll, double wager) {
System.out.printf("%1$ d - NATURAL....A WINNER!!!!%n", roll);
System.out.printf("%1$ d PAYS EVEN MONEY, YOU WIN $%2$3.2f%n", roll, wager);
return wager;
}
public static void winningsUpdate(double winnings) {
System.out.println(switch ((int) Math.signum(winnings)) {
case 1 -> "YOU ARE NOW AHEAD $%3.2f".formatted(winnings);
case 0 -> "YOU ARE NOW EVEN AT 0";
default -> "YOU ARE NOW UNDER $%3.2f".formatted(-winnings);
});
}
public static void winningsReport(double winnings) {
System.out.println(
switch ((int) Math.signum(winnings)) {
case 1 -> "CONGRATULATIONS---YOU CAME OUT A WINNER. COME AGAIN!";
case 0 -> "CONGRATULATIONS---YOU CAME OUT EVEN, NOT BAD FOR AN AMATEUR";
default -> "TOO BAD, YOU ARE IN THE HOLE. COME AGAIN.";
}
);
}
public static boolean stillInterested(double winnings) {
System.out.print(" IF YOU WANT TO PLAY AGAIN PRINT 5 IF NOT PRINT 2 ");
int fiveOrTwo = (int)getInput();
winningsUpdate(winnings);
return fiveOrTwo == 5;
}
public static double getWager() {
System.out.print("INPUT THE AMOUNT OF YOUR WAGER. ");
return getInput();
}
public static double getInput() {
Scanner scanner = new Scanner(System.in);
System.out.print("> ");
while (true) {
try {
return scanner.nextDouble();
} catch (Exception ex) {
try {
scanner.nextLine(); // flush whatever this non number stuff is.
} catch (Exception ns_ex) { // received EOF (ctrl-d or ctrl-z if windows)
System.out.println("END OF INPUT, STOPPING PROGRAM.");
System.exit(1);
}
}
System.out.println("!NUMBER EXPECTED - RETRY INPUT LINE");
System.out.print("> ");
}
}
}
+7 -17
View File
@@ -42,6 +42,11 @@ function tab(space)
return str;
}
function roll()
{
return Math.floor(6 * Math.random())+1 + Math.floor(6 * Math.random())+1;
}
// Main program
async function main()
{
@@ -52,22 +57,11 @@ async function main()
print("\n");
r = 0;
print("2,3,12 ARE LOSERS: 4,5,6,8,9,10 ARE POINTS: 7,11 ARE NATURAL WINNERS.\n");
t = 1;
print("PICK A NUMBER AND INPUT TO ROLL DICE");
z = parseInt(await input());
do {
x = Math.random();
t++;
} while (t <= z) ;
while (1) {
print("INPUT THE AMOUNT OF YOUR WAGER.");
f = parseInt(await input());
print("I WILL NOW THROW THE DICE\n");
do {
e = Math.floor(7 * Math.random());
s = Math.floor(7 * Math.random());
x = e + s;
} while (x == 0 || x == 1) ;
x = roll();
if (x == 7 || x == 11) {
print(x + " - NATURAL....A WINNER!!!!\n");
print(x + " PAYS EVEN MONEY, YOU WIN " + f + " DOLLARS\n");
@@ -83,11 +77,7 @@ async function main()
} else {
print(x + " IS THE POINT. I WILL ROLL AGAIN\n");
while (1) {
do {
h = Math.floor(7 * Math.random());
q = Math.floor(7 * Math.random());
o = h + q;
} while (o == 0 || o == 1) ;
o = roll();
if (o == 7) {
print(o + " - CRAPS, YOU LOSE.\n");
print("YOU LOSE $" + f + "\n");
+69 -70
View File
@@ -1,59 +1,60 @@
#!/usr/bin/ruby
class DepthCharge
def run_game
output_title()
while true
printf("----------\n")
print_instructions()
setup_game()
printf("\n")
game_loop()
break if ! get_input_another_game()
output_title
loop do
puts "----------"
print_instructions
setup_game
puts
game_loop
break unless get_input_another_game
end
printf("OK. HOPE YOU ENJOYED YOURSELF.\n")
puts "OK. HOPE YOU ENJOYED YOURSELF."
end
def output_title
printf("--- DEPTH CHARGE ---\n")
printf("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n")
printf("\n")
puts "--- DEPTH CHARGE ---"
puts "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"
puts
end
def get_input_y_or_n(message)
while true
print(message)
loop do
print message
value = gets.chomp
if (value == 'Y' || value == 'y')
if value.downcase == "y"
return true
elsif value == 'N' || value == 'n'
elsif value.downcase == "n"
return false
end
printf("PLEASE ENTER Y/y OR N/n...\n\n")
puts "PLEASE ENTER Y/y OR N/n..."
puts
end
end
def get_input_positive_integer(message)
while true
print(message)
loop do
print message
value = gets.chomp
if (value == 'd')
debug_game()
if value == "d"
debug_game
next
end
the_input = Integer(value) rescue nil
the_input = Integer(value) rescue 0
if the_input == nil || the_input < 1
printf("PLEASE ENTER A POSITIVE NUMBER\n\n")
if the_input < 1
puts "PLEASE ENTER A POSITIVE NUMBER"
puts
next
end
return the_input
@@ -61,42 +62,39 @@ class DepthCharge
end
def print_instructions
printf( <<~INSTRUCTIONS
YOU ARE THE CAPTAIN OF THE DESTROYER USS COMPUTER
AN ENEMY SUB HAS BEEN CAUSING YOU TROUBLE. YOUR
MISSION IS TO DESTROY IT.
puts <<~INSTRUCTIONS
YOU ARE THE CAPTAIN OF THE DESTROYER USS COMPUTER
AN ENEMY SUB HAS BEEN CAUSING YOU TROUBLE. YOUR
MISSION IS TO DESTROY IT.
SPECIFY DEPTH CHARGE EXPLOSION POINT WITH A
TRIO OF NUMBERS -- THE FIRST TWO ARE THE
SURFACE COORDINATES (X, Y):
WEST < X < EAST
SOUTH < Y < NORTH
SPECIFY DEPTH CHARGE EXPLOSION POINT WITH A
TRIO OF NUMBERS -- THE FIRST TWO ARE THE
SURFACE COORDINATES (X, Y):
WEST < X < EAST
SOUTH < Y < NORTH
THE THIRD IS THE DEPTH (Z):
SHALLOW < Z < DEEP
THE THIRD IS THE DEPTH (Z):
SHALLOW < Z < DEEP
GOOD LUCK !
GOOD LUCK !
INSTRUCTIONS
)
end
def debug_game
printf("@enemy_x: %d\n", @enemy_x)
printf("@enemy_y: %d\n", @enemy_y)
printf("@enemy_z: %d\n", @enemy_z)
printf("@num_tries: %d\n", @num_tries)
printf("@trial: %d\n", @trial)
printf("\n")
puts "@enemy_x: %d" % @enemy_x
puts "@enemy_y: %d" % @enemy_y
puts "@enemy_z: %d" % @enemy_z
puts "@num_tries: %d" % @num_tries
puts "@trial: %d" % @trial
puts
end
def setup_game
@search_area_dimension = get_input_positive_integer("DIMENSION OF SEARCH AREA: ")
@num_tries = Integer(
Math.log(@search_area_dimension)/Math.log(2) + 1
)
setup_enemy()
@num_tries = Integer(Math.log(@search_area_dimension) / Math.log(2) + 1)
setup_enemy
end
def setup_enemy
@@ -113,32 +111,34 @@ GOOD LUCK !
@shot_y = get_input_positive_integer("Y: ")
@shot_z = get_input_positive_integer("Z: ")
if (
(@enemy_x - @shot_x).abs \
+ (@enemy_y - @shot_y).abs \
+ (@enemy_z - @shot_z).abs \
== 0
)
you_win()
distance = (@enemy_x - @shot_x).abs +
(@enemy_y - @shot_y).abs +
(@enemy_z - @shot_z).abs
if distance == 0
you_win
return
else
missed_shot()
missed_shot
end
end
printf("\n")
you_lose()
puts
you_lose
end
def output_game_status
printf("YOU HAVE %d SHOTS REMAINING.\n", @num_tries - @trial + 1)
printf("TRIAL \#%d\n", @trial)
puts "YOU HAVE %d SHOTS REMAINING." % @num_tries - @trial + 1
puts "TRIAL \#%d" % @trial
end
def you_win
printf("\nB O O M ! ! YOU FOUND IT IN %d TRIES!\n\n", @trial )
puts "\nB O O M ! ! YOU FOUND IT IN %d TRIES!" % @trial
puts
end
def missed_shot
missed_directions = []
@@ -160,14 +160,13 @@ GOOD LUCK !
missed_directions.push('TOO SHALLOW')
end
printf("SONAR REPORTS SHOT WAS: \n")
printf("%s\n", "\t" + missed_directions.join("\n\t"))
puts "SONAR REPORTS SHOT WAS: "
puts "\t#{missed_directions.join("\n\t")}"
end
def you_lose
printf("YOU HAVE BEEN TORPEDOED! ABANDON SHIP!\n")
printf("THE SUBMARINE WAS AT %d %d %d\n", @enemy_x, @enemy_y, @enemy_z)
puts "YOU HAVE BEEN TORPEDOED! ABANDON SHIP!"
puts "THE SUBMARINE WAS AT %d %d %d" % [@enemy_x, @enemy_y, @enemy_z]
end
def get_input_another_game
@@ -176,4 +175,4 @@ GOOD LUCK !
end
game = DepthCharge.new
game.run_game()
game.run_game
+121
View File
@@ -0,0 +1,121 @@
#!/usr/bin/perl
use strict;
print ' 'x 33 . "GOMOKO\n";
print ' 'x 15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n";
print "\n"; print "\n"; print "\n";
#my @A;
print "WELCOME TO THE ORIENTAL GAME OF GOMOKO.\n";
print "\n"; print "THE GAME IS PLAYED ON AN N BY N GRID OF A SIZE\n";
print "THAT YOU SPECIFY. DURING YOUR PLAY, YOU MAY COVER ONE GRID\n";
print "INTERSECTION WITH A MARKER. THE OBJECT OF THE GAME IS TO GET\n";
print "5 ADJACENT MARKERS IN A ROW -- HORIZONTALLY, VERTICALLY, OR\n";
print "DIAGONALLY. ON THE BOARD DIAGRAM, YOUR MOVES ARE MARKED\n";
print "WITH A '1' AND THE COMPUTER MOVES WITH A '2'.\n";
print "\n"; print "THE COMPUTER DOES NOT KEEP TRACK OF WHO HAS WON.\n";
print "TO END THE GAME, TYPE -1,-1 FOR YOUR MOVE.\n"; print "\n";
my $Ret;
my $I;
my $J;
my @Board;
my $Size= 0;
while (1) {
do {
$Size= 0;
print "WHAT IS YOUR BOARD SIZE (MIN 7/ MAX 19)"; print "? "; chomp($Size = uc(<STDIN>));
if ($Size<7 || $Size>19) {
$Size=0;
print "I SAID, THE MINIMUM IS 7, THE MAXIMUM IS 19.\n";
}
} until ($Size);
#==> Reset Board to zeroes...
for (my $I=1; $I<=$Size; $I++) {
for (my $J=1; $J<=$Size; $J++) {
$Board[$I][$J]= 0;
}
}
print "\n"; print "WE ALTERNATE MOVES. YOU GO FIRST...\n"; print "\n";
while (1) {
do {
print "YOUR PLAY (I,J)"; print "? "; chomp(my $Inp = uc(<STDIN>));
($I, $J)= split(",", $Inp);
print "\n";
if ($I==-1) { last; }
$Ret= &ValidMove($I, $J, 1);
} until ($Ret==1);
if ($I==-1) { last; }
$Board[$I][$J]= 1;
my $X;
my $Y;
my $Found=0;
# REM *** COMPUTER TRIES AN INTELLIGENT MOVE ***
#==> Too complex, original basic code seems only move below user.
$Ret= &ValidMove($I+1, $J);
if ($Ret==1) {
$Found=1;
$X= $I+1;
$Y= $J;
}
while($Found==0) {
# REM *** COMPUTER TRIES A RANDOM MOVE ***
$X= int($Size*rand(1)+1);
$Y= int($Size*rand(1)+1);
$Ret= &ValidMove($X, $Y, 2);
if ($Ret==1) { $Found= 1; }
};
$Board[$X][$Y]=2;
&ShowBoard();
}
print "\n"; print "THANKS FOR THE GAME!!\n";
print "PLAY AGAIN (1 FOR YES, 0 FOR NO)"; print "? "; chomp(my $Q = uc(<STDIN>));
if ($Q==0) { last; }
}
exit;
sub ShowBoard {
for (my $I=1; $I<=$Size; $I++) {
print " ";
for (my $J=1; $J<=$Size; $J++) {
print "$Board[$I][$J] ";
}
print "\n";
}
print "\n";
return;
}
sub ValidMove {
my ($X, $Y, $Val)= @_;
if ($X<1 || $X>$Size || $Y<1 || $Y>$Size) {
if ($Val==1) { print "ILLEGAL MOVE. TRY AGAIN...\n"; }
return 0;
}
if ($Board[$X][$Y]!=0) {
if ($Val==1) { print "SQUARE OCCUPIED. TRY AGAIN...\n"; }
return 0;
}
#$Board[$X][$Y]= $Val;
return 1;
}
+3 -2
View File
@@ -137,7 +137,7 @@ while Z < 11: # line 270. main loop. while the year is less than 11
# REM *** ENOUGH GRAIN FOR SEED?
bad_input_710(S)
D = -99
elif D >= 10 * P:
elif D > 10 * P:
# REM *** ENOUGH PEOPLE TO TEND THE CROPS?
print("BUT YOU HAVE ONLY", P, "PEOPLE TO TEND THE FIELDS! NOW THEN,")
D = -99
@@ -154,7 +154,8 @@ while Z < 11: # line 270. main loop. while the year is less than 11
if int(C / 2) == C / 2: # even number. 50/50 chance
# REM *** RATS ARE RUNNING WILD!!
E = int(S / C) # calc losses due to rats, based on previous random number
S = S - E + H # deduct losses from stores
S = S - E + H # deduct losses from stores
C = gen_random()
# REM *** LET'S HAVE SOME BABIES
+127
View File
@@ -0,0 +1,127 @@
def new_board():
# Using a dictionary in python to store the board, since we are not including all numbers within a given range.
board = {}
for i in [13, 14, 15, 22, 23, 24, 29, 30, 31, 32, 33, 34, 35, 38, 39, 40, 42, 43, 44, 47, 48, 49, 50, 51, 52, 53, 58, 59, 60, 67, 68, 69]:
board[i] = '!'
board[41] = 'O'
return board
def print_instructions():
print("""
HERE IS THE BOARD:
! ! !
13 14 15
! ! !
22 23 24
! ! ! ! ! ! !
29 30 31 32 33 34 35
! ! ! ! ! ! !
38 39 40 41 42 43 44
! ! ! ! ! ! !
47 48 49 50 51 52 53
! ! !
58 59 60
! ! !
67 68 69
TO SAVE TYPING TIME, A COMPRESSED VERSION OF THE GAME BOARD
WILL BE USED DURING PLAY. REFER TO THE ABOVE ONE FOR PEG
NUMBERS. OK, LET'S BEGIN.
""")
def print_board(board):
"""Prints the boards using indexes in the passed parameter"""
print(" " * 2 + board[13] + board[14] + board[15])
print(" " * 2 + board[22] + board[23] + board[24])
print(board[29] + board[30] + board[31] + board[32] + board[33] + board[34] + board[35])
print(board[38] + board[39] + board[40] + board[41] + board[42] + board[43] + board[44])
print(board[47] + board[48] + board[49] + board[50] + board[51] + board[52] + board[53])
print(" " * 2 + board[58] + board[59] + board[60])
print(" " * 2 + board[67] + board[68] + board[69])
def play_game():
# Create new board
board = new_board()
# Main game loop
while not is_game_finished(board):
print_board(board)
while not move(board):
print("ILLEGAL MOVE! TRY AGAIN")
# Check peg count and print the user's score
peg_count = 0
for key in board.keys():
if board[key] == '!':
peg_count += 1
print("YOU HAD " + str(peg_count) + " PEGS REMAINING")
if peg_count == 1:
print("BRAVO! YOU MADE A PERFECT SCORE!")
print("SAVE THIS PAPER AS A RECORD OF YOUR ACCOMPLISHMENT!")
def move(board):
"""Queries the user to move. Returns false if the user puts in an invalid input or move, returns true if the move was successful"""
start_input = input("MOVE WHICH PIECE? ")
if not start_input.isdigit():
return False
start = int(start_input)
if start not in board or board[start] != '!':
return False
end_input = input("TO WHERE? ")
if not end_input.isdigit():
return False
end = int(end_input)
if end not in board or board[end] != 'O':
return False
difference = abs(start - end)
center = (end + start) / 2
if (difference == 2 or difference == 18) and board[end] == 'O' and board[center] == '!':
board[start] = 'O'
board[center] = 'O'
board[end] = '!'
return True
else:
return False
def main():
print(" " * 33 + "H-I-Q")
print(" " * 15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")
print_instructions()
play_game()
def is_game_finished(board):
# Checks all locations and whether or not a move is possible at that location.
for pos in board.keys():
if board[pos] == '!':
for space in [1,9]:
# Checks if the next location has a peg
nextToPeg = ((pos + space) in board) and board[pos + space] == '!'
# Checks both going forward (+ location) or backwards (-location)
hasMovableSpace = (not ((pos - space) in board and board[pos - space] == '!')) or (not ((pos + space * 2) in board and board[pos + space * 2] == '!'))
if nextToPeg and hasMovableSpace:
return False
return True
if __name__ == "__main__":
main()
+2
View File
@@ -1,3 +1,5 @@
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
Conversion to [Python](https://www.python.org/about/)
[Implementation](./High_IQ.py) by [Thomas Kwashnak](https://github.com/LittleTealeaf)
+509
View File
@@ -0,0 +1,509 @@
# Created by https://www.toptal.com/developers/gitignore/api/dotnetcore,rider,visualstudio,visualstudiocode
# Edit at https://www.toptal.com/developers/gitignore?templates=dotnetcore,rider,visualstudio,visualstudiocode
### DotnetCore ###
# .NET Core build folders
bin/
obj/
# Common node modules locations
/node_modules
/wwwroot/node_modules
### Rider ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide
# Support for Project snippet scope
### VisualStudio ###
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
*.code-workspace
# Local History for Visual Studio Code
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml
### VisualStudio Patch ###
# Additional files built by Visual Studio
# End of https://www.toptal.com/developers/gitignore/api/dotnetcore,rider,visualstudio,visualstudiocode
+9
View File
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
+16
View File
@@ -0,0 +1,16 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Life", "Life.csproj", "{28B02688-78D1-4B3E-B998-BCC78C292D03}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{28B02688-78D1-4B3E-B998-BCC78C292D03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{28B02688-78D1-4B3E-B998-BCC78C292D03}.Debug|Any CPU.Build.0 = Debug|Any CPU
{28B02688-78D1-4B3E-B998-BCC78C292D03}.Release|Any CPU.ActiveCfg = Release|Any CPU
{28B02688-78D1-4B3E-B998-BCC78C292D03}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
+324
View File
@@ -0,0 +1,324 @@
using System.Text;
const int maxWidth = 70;
const int maxHeight = 24;
Console.WriteLine("ENTER YOUR PATTERN:");
var pattern = new Pattern(ReadPattern(limitHeight: maxHeight).ToArray());
var minX = 10 - pattern.Height / 2;
var minY = 34 - pattern.Width / 2;
var maxX = maxHeight - 1;
var maxY = maxWidth - 1;
var matrix = new Matrix(height: maxHeight, width: maxWidth);
var simulation = InitializeSimulation(pattern, matrix);
PrintHeader();
ProcessSimulation();
IEnumerable<string> ReadPattern(int limitHeight)
{
for (var i = 0; i < limitHeight; i++)
{
var input = Console.ReadLine();
if (input.ToUpper() == "DONE")
{
break;
}
// In the original version, BASIC would trim the spaces in the beginning of an input, so the original
// game allowed you to input an '.' before the spaces to circumvent this limitation. This behavior was
// kept for compatibility.
if (input.StartsWith('.'))
yield return input.Substring(1, input.Length - 1);
yield return input;
}
}
void PrintHeader()
{
void PrintCentered(string text)
{
const int pageWidth = 64;
var spaceCount = (pageWidth - text.Length) / 2;
Console.Write(new string(' ', spaceCount));
Console.WriteLine(text);
}
PrintCentered("LIFE");
PrintCentered("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
Console.WriteLine();
Console.WriteLine();
Console.WriteLine();
}
Simulation InitializeSimulation(Pattern pattern, Matrix matrixToInitialize) {
var newSimulation = new Simulation();
// transcribes the pattern to the middle of the simulation and counts initial population
for (var x = 0; x < pattern.Height; x++)
{
for (var y = 0; y < pattern.Width; y++)
{
if (pattern.Content[x][y] == ' ')
continue;
matrixToInitialize[minX + x, minY + y] = CellState.Stable;
newSimulation.IncreasePopulation();
}
}
return newSimulation;
}
TimeSpan GetPauseBetweenIterations()
{
if (args.Length != 2) return TimeSpan.Zero;
var parameter = args[0].ToLower();
if (parameter.Contains("wait"))
{
var value = args[1];
if (int.TryParse(value, out var sleepMilliseconds))
return TimeSpan.FromMilliseconds(sleepMilliseconds);
}
return TimeSpan.Zero;
}
void ProcessSimulation()
{
var pauseBetweenIterations = GetPauseBetweenIterations();
var isInvalid = false;
while (true)
{
var invalidText = isInvalid ? "INVALID!" : "";
Console.WriteLine($"GENERATION: {simulation.Generation}\tPOPULATION: {simulation.Population} {invalidText}");
simulation.StartNewGeneration();
var nextMinX = maxHeight - 1;
var nextMinY = maxWidth - 1;
var nextMaxX = 0;
var nextMaxY = 0;
var matrixOutput = new StringBuilder();
// prints the empty lines before search area
for (var x = 0; x < minX; x++)
{
matrixOutput.AppendLine();
}
// refreshes the matrix and updates search area
for (var x = minX; x <= maxX; x++)
{
var printedLine = Enumerable.Repeat(' ', maxWidth).ToList();
for (var y = minY; y <= maxY; y++)
{
if (matrix[x, y] == CellState.Dying)
{
matrix[x, y] = CellState.Empty;
continue;
}
if (matrix[x, y] == CellState.New)
{
matrix[x, y] = CellState.Stable;
}
else if (matrix[x, y] != CellState.Stable)
{
continue;
}
printedLine[y] = '*';
nextMinX = Math.Min(x, nextMinX);
nextMaxX = Math.Max(x, nextMaxX);
nextMinY = Math.Min(y, nextMinY);
nextMaxY = Math.Max(y, nextMaxY);
}
matrixOutput.AppendLine(string.Join(separator: null, values: printedLine));
}
// prints empty lines after search area
for (var x = maxX + 1; x < maxHeight; x++)
{
matrixOutput.AppendLine();
}
Console.Write(matrixOutput);
void UpdateSearchArea()
{
minX = nextMinX;
maxX = nextMaxX;
minY = nextMinY;
maxY = nextMaxY;
const int limitX = 21;
const int limitY = 67;
if (minX < 2)
{
minX = 2;
isInvalid = true;
}
if (maxX > limitX)
{
maxX = limitX;
isInvalid = true;
}
if (minY < 2)
{
minY = 2;
isInvalid = true;
}
if (maxY > limitY)
{
maxY = limitY;
isInvalid = true;
}
}
UpdateSearchArea();
for (var x = minX - 1; x <= maxX + 1; x++)
{
for (var y = minY - 1; y <= maxY + 1; y++)
{
int CountNeighbors()
{
var neighbors = 0;
for (var i = x - 1; i <= x + 1; i++)
{
for (var j = y - 1; j <= y + 1; j++)
{
if (matrix[i, j] == CellState.Stable || matrix[i, j] == CellState.Dying)
neighbors++;
}
}
return neighbors;
}
var neighbors = CountNeighbors();
if (matrix[x, y] == CellState.Empty)
{
if (neighbors == 3)
{
matrix[x, y] = CellState.New;
simulation.IncreasePopulation();
}
}
else if (neighbors is < 3 or > 4)
{
matrix[x, y] = CellState.Dying;
}
else
{
simulation.IncreasePopulation();
}
}
}
// expands search area to accommodate new cells
minX--;
minY--;
maxX++;
maxY++;
if (pauseBetweenIterations > TimeSpan.Zero)
Thread.Sleep(pauseBetweenIterations);
}
}
public class Pattern
{
public string[] Content { get; }
public int Height { get; }
public int Width { get; }
public Pattern(IReadOnlyCollection<string> patternLines)
{
Height = patternLines.Count;
Width = patternLines.Max(x => x.Length);
Content = NormalizeWidth(patternLines);
}
private string[] NormalizeWidth(IReadOnlyCollection<string> patternLines)
{
return patternLines
.Select(x => x.PadRight(Width, ' '))
.ToArray();
}
}
/// <summary>
/// Indicates the state of a given cell in the simulation.
/// </summary>
internal enum CellState
{
Empty = 0,
Stable = 1,
Dying = 2,
New = 3
}
public class Simulation
{
public int Generation { get; private set; }
public int Population { get; private set; }
public void StartNewGeneration()
{
Generation++;
Population = 0;
}
public void IncreasePopulation()
{
Population++;
}
}
/// <summary>
/// This class was created to aid debugging, through the implementation of the ToString() method.
/// </summary>
class Matrix
{
private readonly CellState[,] _matrix;
public Matrix(int height, int width)
{
_matrix = new CellState[height, width];
}
public CellState this[int x, int y]
{
get => _matrix[x, y];
set => _matrix[x, y] = value;
}
public override string ToString()
{
var stringBuilder = new StringBuilder();
for (var x = 0; x < _matrix.GetLength(0); x++)
{
for (var y = 0; y < _matrix.GetLength(1); y++)
{
var character = _matrix[x, y] == 0 ? " ": ((int)_matrix[x, y]).ToString();
stringBuilder.Append(character);
}
stringBuilder.AppendLine();
}
return stringBuilder.ToString();
}
}
+68
View File
@@ -1,3 +1,71 @@
# Life
An implementation of John Conway's popular cellular automaton, also know as **Conway's Game of Life**. The original source was downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html).
Ported by Dyego Alekssander Maas.
## How to run
This program requires you to install [.NET 6 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/6.0). After installed, you just need to run `dotnet run` from this directory in the terminal.
## Know more about Conway's Game of Life
You can find more about Conway's Game of Life on this page of the [Cornell Math Explorers' Club](http://pi.math.cornell.edu/~lipa/mec/lesson6.html), alongside many examples of patterns you can try.
### Optional parameters
Optionally, you can run this program with the `--wait 1000` argument, the number being the time in milliseconds
that the application will pause between each iteration. This is enables you to watch the simulation unfolding. By default, there is no pause between iterations.
The complete command would be `dotnet run --wait 1000`.
## Entering patterns
Once running the game, you are expected to enter a pattern. This pattern consists of multiple lines of text with either **spaces** or **some character**, usually an asterisk (`*`).
Spaces represent empty cells. Asterisks represent alive cells.
After entering the pattern, you need to enter the word "DONE". It is not case sensitive. An example of pattern would be:
```
*
***
DONE
```
### Some patterns you could try
```
*
***
```
```
*
***
```
```
**
**
```
```
*
*
*
```
This one is known as **glider**:
```
***
*
*
```
## Instructions to the port
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
+96 -96
View File
@@ -1,96 +1,96 @@
#!/usr/bin/perl
use strict;
use warnings;
# global variables defined here
my(@MUGWUMP) = ();
# subroutines defined here
# init_mugwump: pick the random places for the Mugwumps
sub init_mugwump() {
@MUGWUMP = ();
for (1 .. 4) {
push @MUGWUMP, [ int(rand 10), int(rand 10) ];
}
}
# main code starts here
# print introductory text
print <<HERE;
Mugwump
Creative Computing Morristown, New Jersey
The object of this game is to find four Mugwumps
hidden on a 10 by 10 grid. Homebase is position 0,0.
Any guess you make must be two numbers with each
number between 0 and 9, inclusive. First number
is distance to right of homebase and second number
is distance above homebase.
You get 10 tries. After each try, I will tell
you how far you are from each Mugwump.
HERE
# PLAY block implements a complete game, and the
# continue block prints the "let's play again" msg
PLAY: while (1) {
init_mugwump();
TURN: for my $turn (1 .. 10) {
printf("\nTurn no %d -- what is your guess ? ", $turn);
# Note that parsing of input is rudimentary, in keeping with the
# spirit of the original BASIC program. Increased input checks
# would be a good place to start working on this program!
chomp(my $in = <STDIN>);
my($M,$N) = split(/,/,$in);
$M = int($M);
$N = int($N);
for my $i (0 .. $#MUGWUMP) {
# -1 indicates a Mugwump that was already found
next if $MUGWUMP[$i]->[0] == -1;
if ($MUGWUMP[$i]->[0] == $M && $MUGWUMP[$i]->[1] == $N) {
$MUGWUMP[$i]->[0] = -1;
printf("You have found Mugwump %d\n", $i+1);
} else {
my $d = sqrt(($MUGWUMP[$i]->[0] - $M) ** 2 + ($MUGWUMP[$i]->[1] - $N) ** 2);
printf("You are %.1f units away from Mugwump %d\n", $d, $i+1);
}
}
# If a Mugwump still has not been found,
# go to the next turn
for my $j (0 .. $#MUGWUMP) {
if ($MUGWUMP[$j]->[0] != -1) {
next TURN;
}
}
# You win!
printf("You got all of them in %d %s!\n\n", $turn, ($turn == 1 ? 'turn' : 'turns'));
# Pass execution down to the continue block
next PLAY;
} # end of TURN loop
print "\nSorry, that's 10 tries. Here's where they're hiding:\n";
for my $i (0 .. $#MUGWUMP) {
printf("Mugwump %d is at (%d, %d)\n", $i+1, $MUGWUMP[$i]->[0], $MUGWUMP[$i]->[1])
if $MUGWUMP[$i]->[0] != -1;
}
}
continue {
print "\nThat was fun! Let's play again.......\n";
print "Four more Mugwumps are now in hiding.\n\n";
}
#!/usr/bin/perl
use strict;
use warnings;
# global variables defined here
my(@MUGWUMP) = ();
# subroutines defined here
# init_mugwump: pick the random places for the Mugwumps
sub init_mugwump() {
@MUGWUMP = ();
for (1 .. 4) {
push @MUGWUMP, [ int(rand 10), int(rand 10) ];
}
}
# main code starts here
# print introductory text
print <<HERE;
Mugwump
Creative Computing Morristown, New Jersey
The object of this game is to find four Mugwumps
hidden on a 10 by 10 grid. Homebase is position 0,0.
Any guess you make must be two numbers with each
number between 0 and 9, inclusive. First number
is distance to right of homebase and second number
is distance above homebase.
You get 10 tries. After each try, I will tell
you how far you are from each Mugwump.
HERE
# PLAY block implements a complete game, and the
# continue block prints the "let's play again" msg
PLAY: while (1) {
init_mugwump();
TURN: for my $turn (1 .. 10) {
printf("\nTurn no %d -- what is your guess ? ", $turn);
# Note that parsing of input is rudimentary, in keeping with the
# spirit of the original BASIC program. Increased input checks
# would be a good place to start working on this program!
chomp(my $in = <STDIN>);
my($M,$N) = split(/,/,$in);
$M = int($M);
$N = int($N);
for my $i (0 .. $#MUGWUMP) {
# -1 indicates a Mugwump that was already found
next if $MUGWUMP[$i]->[0] == -1;
if ($MUGWUMP[$i]->[0] == $M && $MUGWUMP[$i]->[1] == $N) {
$MUGWUMP[$i]->[0] = -1;
printf("You have found Mugwump %d\n", $i+1);
} else {
my $d = sqrt(($MUGWUMP[$i]->[0] - $M) ** 2 + ($MUGWUMP[$i]->[1] - $N) ** 2);
printf("You are %.1f units away from Mugwump %d\n", $d, $i+1);
}
}
# If a Mugwump still has not been found,
# go to the next turn
for my $j (0 .. $#MUGWUMP) {
if ($MUGWUMP[$j]->[0] != -1) {
next TURN;
}
}
# You win!
printf("You got all of them in %d %s!\n\n", $turn, ($turn == 1 ? 'turn' : 'turns'));
# Pass execution down to the continue block
next PLAY;
} # end of TURN loop
print "\nSorry, that's 10 tries. Here's where they're hiding:\n";
for my $i (0 .. $#MUGWUMP) {
printf("Mugwump %d is at (%d, %d)\n", $i+1, $MUGWUMP[$i]->[0], $MUGWUMP[$i]->[1])
if $MUGWUMP[$i]->[0] != -1;
}
}
continue {
print "\nThat was fun! Let's play again.......\n";
print "Four more Mugwumps are now in hiding.\n\n";
}
+282
View File
@@ -0,0 +1,282 @@
puts "NIM".center(80)
puts"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY".center(80)
puts "\n\n\n"
#210 DIM A(100),B(100,10),D(2)
$pileArray = Array.new[100]
$bArray = Array.new
$dArray = Array.new[2]
$winOption = 0 # take-last option
$numberOfPiles = 1
$c = 0
$e = 0
$f = 0
$g = 0
$h = 0
def displayTheRules
puts "THE GAME IS PLAYED WITH A NUMBER OF PILES OF OBJECTS."
puts "ANY NUMBER OF OBJECTS ARE REMOVED FROM ONE PILE BY YOU AND"
puts "THE MACHINE ALTERNATELY. ON YOUR TURN, YOU MAY TAKE"
puts "ALL THE OBJECTS THAT REMAIN IN ANY PILE, BUT YOU MUST"
puts "TAKE AT LEAST ONE OBJECT, AND YOU MAY TAKE OBJECTS FROM"
puts "ONLY ONE PILE ON A SINGLE TURN. YOU MUST SPECIFY WHETHER"
puts "WINNING IS DEFINED AS TAKING OR NOT TAKING THE LAST OBJECT,"
puts "THE NUMBER OF PILES IN THE GAME, AND HOW MANY OBJECTS ARE"
puts "ORIGINALLY IN EACH PILE. EACH PILE MAY CONTAIN A"
puts "DIFFERENT NUMBER OF OBJECTS."
puts "THE MACHINE WILL SHOW ITS MOVE BY LISTING EACH PILE AND THE"
puts "NUMBER OF OBJECTS REMAINING IN THE PILES AFTER EACH OF ITS"
puts "MOVES."
end
def sub1570
$z=0
for i in 1..$numberOfPiles do
if $pileArray[i] != 0 then
return
end
$z=1
return
end
end
def playAnother
put "do you want to play another game";
return gets.strip.ucase == "YES"
end
puts "THIS IS THE GAME OF NIM."
print "DO YOU WANT INSTRUCTIONS?"
240
wantInstructions = gets.strip.upcase
if wantInstructions == "YES" then
displayTheRules
end
#250 IF Z$="NO" THEN 440
#260 IF Z$="no" THEN 440
#270 IF Z$="YES" THEN displayTheRules
#280 IF Z$="yes" THEN displayTheRules
#290 PRINT "PLEASE ANSWER YES OR NO"
#300 GOTO 240
def sub490 # get number of piles
print "ENTER NUMBER OF PILES:"
while $numberOfPiles < 0 && $numberOfPiles <= 100 do
$numberOfPiles = gets.strip.to_i
end
end
def getPileSizes
puts "ENTER PILE SIZES:"
for i in 1..$numberOfPiles do
print i
while true do
$pileArray[i] = gets.strip.to_i
if $pileArray[i] < 2000 && $pileArray[i] > 0 then
break
end
end
end
end
def sub440 # get win option
puts ""
$winOption = 0
while $winOption != 1 && q != 2 do
puts "ENTER WIN OPTION - 1 TO TAKE LAST, 2 TO AVOID LAST"
$winOption = gets.strip.to_i
end
end
puts "DO YOU WANT TO MOVE FIRST?";
#630 INPUT Q9$
moveFirst = ""
while moveFirst != "YES" && moveFirst != "NO" do
moveFirst = gets.strip.upcase
case moveFirst
when "YES"
yourMove
when "NO"
machineMove
end
end
#640 IF Q9$="YES" THEN 1450
#650 IF Q9$="yes" THEN 1450
#660 IF Q9$="NO" THEN 700
#670 IF Q9$="no" THEN 700
#680 PRINT "PLEASE ANSWER YES OR NO."
#690 GOTO 630
def machineMove
if $winOption == 1 then
sub940 # take last
end
$c=0
for i in 1..$numberOfPiles do
if $pileArray[i] != 0 then 770
$c=$c+1
if $c == 3 then
sub840
end
$dArray[$c-1]=i
end
end
if $c == 2 then
sub920
end
if $pileArray[$dArray[0]] > 1 then
machineWins
end
machineLoses
end
def machineLoses
puts "MACHINE LOSES"
# 810 GOTO playAnother
if playAnother then
sub440 # loop for another
end
end
def machineWins
puts "MACHINE WINS"
# 830 GOTO playAnother
if playAnother then
sub440 # loop for another
end
end
def sub840
$c=0
for i in 1..$numberOfPiles do
if $pileArray[i] > 1 then
sub940
end
if $pileArray[i] == 0 then 890
$c=$c+1
end
if $c/2 != ($c/2).to_i then
machineLoses
end
sub940 # goto
end
end
def sub920
if $pileArray[$dArray[0]] == 1 then
machineWins
end
if $pileArray[$dArray[1]] == 1 then
machineWins
end
end
def sub940
for i in 1..$numberOfPiles do
e=$pileArray[i]
for j in 0..10 do
$f = $e/2
$bArray[i][j] = 2*($f-($f.to_i))
$e = $f.to_i
end
end
end
#for j in 10..0 STEP -1 do
10..0.step(-1).each do|index|
$c=0
$h=0
for i in 1..$numberOfPiles do
if $bArray[i][index] != 0 then
$c=$c+1
if $pileArray[i] > $h then
$h = $pileArray[i]
$g = i
end
end
end
end
if $c/2 != ($c/2).to_i then 1190
end
$e = rand($numberOfPiles).to_i
#if $pileArray[$e] == 0 then 1140
$f = rand($pileArray[$e]).to_i
$pileArray[$e] = $pileArray[$e]-$f
sub1380
$pileArray[$g]=0
for j in 0..10 do
$bArray[$g][index]=0
$c=0
for i in 1..$numberOfPiles do
if $bArray[i][index] != 0 then
$c=$c+1
end
end
$pileArray[$g]=$pileArray[$g]+2*($c/2-($c/2)).to_i*2^j
end
if $winOption == 1 then
sub1380
end
$c=0
for i in 1..$numberOfPiles do
if $pileArray[i]>1 then
sub1380
end
if $pileArray[i] != 0 then
$c=$c+1
end
if $c/2 == ($c/2).to_i then
sub1380
end
$pileArray[$g] == 1 -$pileArray[$g]
def sub1380
puts "PILE SIZE"
for i in 1..$numberOfPiles do
put i
put $pileArray[i]
end
if $winOption == 2 then # avoid take-last option
yourMove
end
sub1570
if $z == 1 then
machineWins
end
end
def yourMove
put "YOUR MOVE - PILE, NUMBER TO BE REMOVED"
# 1460 INPUT x,y
x = gets.strip.to_i
y = gets.strip.to_i
if x > $numberOfPiles then yourMove
if x < 1 then yourMove
if x != INT(x) then yourMove
if y > $pileArray[x] then yourMove
if y < 1 then
yourMove
end
if y != INT(y) then
yourMove
end
$pileArray[x] = $pileArray[x]-y
sub1570 # gosub
if $z == 1 then
machineLoses
end
# 1560 GOTO 700
end
end
end
end
end
end
+115
View File
@@ -0,0 +1,115 @@
# ONE CHECK
# Port to python by imiro
def tab(x):
return ' '*x
def main():
# Initial instructions
print(tab(30) + "ONE CHECK");
print(tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
print();
print();
print();
print("SOLITAIRE CHECKER PUZZLE BY DAVID AHL");
print();
print("48 CHECKERS ARE PLACED ON THE 2 OUTSIDE SPACES OF A");
print("STANDARD 64-SQUARE CHECKERBOARD. THE OBJECT IS TO");
print("REMOVE AS MANY CHECKERS AS POSSIBLE BY DIAGONAL JUMPS");
print("(AS IN STANDARD CHECKERS). USE THE NUMBERED BOARD TO");
print("INDICATE THE SQUARE YOU WISH TO JUMP FROM AND TO. ON");
print("THE BOARD PRINTED OUT ON EACH TURN '1' INDICATES A");
print("CHECKER AND '0' AN EMPTY SQUARE. WHEN YOU HAVE NO");
print("POSSIBLE JUMPS REMAINING, INPUT A '0' IN RESPONSE TO");
print("QUESTION 'JUMP FROM ?'");
print();
print("HERE IS THE NUMERICAL BOARD:");
print();
while(True):
for j in range(1,64,8):
for i in range(j,j+7):
print(i, end=(' '*(3 if i < 10 else 2)))
print(j+7)
print()
print("AND HERE IS THE OPENING POSITION OF THE CHECKERS.")
print()
(jumps, left) = play_game()
print()
print("YOU MADE " + jumps + " JUMPS AND HAD " + left + " PIECES")
print("REMAINING ON THE BOARD.")
print()
if not(try_again()):
break
print()
print("O.K. HOPE YOU HAD FUN!!")
def play_game():
# Initialize board
# Give more than 64 elements to accomodate 1-based indexing
board = [1]*70
for j in range(19,44,8):
for i in range(j,j+4):
board[i] = 0
jumps = 0
while True:
# print board
for j in range(1,64,8):
for i in range(j,j+7):
print(board[i], end=' ')
print(board[j+7])
print()
while True:
print("JUMP FROM", end=' ')
f = input()
f = int(f)
if f == 0:
break
print("TO", end=' ')
t = input()
t = int(t)
print()
# Check legality of move
f1 = ((f-1) // 8)
f2 = f - 8 * f1
t1 = ((t-1) // 8)
t2 = t - 8 * t1
if (f1 > 7 or t1 > 7 or f2 > 8 or t2 > 8 or abs(f1 - t1) != 2 or
abs(f2 - t2) != 2 or board[(t + f) // 2] == 0 or
board[f] == 0 or board[t] == 1):
print("ILLEGAL MOVE. TRY AGAIN...")
continue
break
if(f == 0):
break
board[t] = 1
board[f] = 0
board[(t+f) // 2] = 0
jumps = jumps + 1
left = 0
for i in range(1,64+1):
left = left + board[i]
return (str(jumps), str(left))
def try_again():
print("TRY AGAIN", end=' ')
answer = input()
if (answer.upper() == "YES"):
return True
elif (answer.upper() == "NO"):
return False
print("PLEASE ANSWER 'YES' OR 'NO'.")
try_again()
if __name__ == '__main__':
main()
@@ -0,0 +1,10 @@
using FsCheck;
namespace Reverse.Tests.Generators
{
public static class PositiveIntegerGenerator
{
public static Arbitrary<int> Generate() =>
Arb.Default.Int32().Filter(x => x > 0);
}
}
@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FsCheck.Xunit" Version="2.16.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<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.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Reverse\Reverse.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,148 @@
using FsCheck.Xunit;
using Reverse.Tests.Generators;
using System;
using System.Linq;
using Xunit;
namespace Reverse.Tests
{
public class ReverserTests
{
[Fact]
public void Constructor_CannotAcceptNumberLessThanZero()
{
Action action = () => new Reverser(0);
Assert.Throws<ArgumentOutOfRangeException>(action);
}
[Property(Arbitrary = new[] { typeof(PositiveIntegerGenerator) })]
public void Constructor_CreatesRandomArrayOfSpecifiedLength(int size)
{
var sut = new TestReverser(size);
Assert.Equal(size, sut.GetArray().Length);
}
[Property(Arbitrary = new[] { typeof(PositiveIntegerGenerator) })]
public void ConstructorArray_MaxElementValueIsEqualToSize(int size)
{
var sut = new TestReverser(size);
Assert.Equal(size, sut.GetArray().Max());
}
[Property(Arbitrary = new[] { typeof(PositiveIntegerGenerator) })]
public void ConstructorArray_ReturnsRandomArrayWithDistinctElements(int size)
{
var sut = new TestReverser(size);
var array = sut.GetArray();
var arrayGroup = array.GroupBy(x => x);
var duplicateFound = arrayGroup.Any(x => x.Count() > 1);
Assert.False(duplicateFound);
}
[Theory]
[InlineData(new int[] { 1 }, new int[] { 1 })]
[InlineData(new int[] { 1, 2 }, new int[] { 2, 1 })]
[InlineData(new int[] { 1, 2, 3 }, new int[] { 3, 2, 1 })]
public void Reverse_WillReverseEntireArray(int[] input, int[] output)
{
var sut = new TestReverser(1);
sut.SetArray(input);
sut.Reverse(input.Length);
Assert.True(sut.GetArray().SequenceEqual(output));
}
[Fact]
public void Reverse_WithSpecifiedIndex_ReversesItemsUpToThatIndex()
{
var input = new int[] { 1, 2, 3, 4 };
var output = new int[] { 2, 1, 3, 4 };
var sut = new TestReverser(1);
sut.SetArray(input);
sut.Reverse(2);
Assert.True(sut.GetArray().SequenceEqual(output));
}
[Fact]
public void Reverse_WithIndexOne_DoesNothing()
{
var input = new int[] { 1, 2 };
var output = new int[] { 1, 2 };
var sut = new TestReverser(1);
sut.SetArray(input);
sut.Reverse(1);
Assert.True(sut.GetArray().SequenceEqual(output));
}
[Fact]
public void Reverse_WithIndexGreaterThanArrayLength_DoesNothing()
{
var input = new int[] { 1, 2 };
var output = new int[] { 1, 2 };
var sut = new TestReverser(1);
sut.SetArray(input);
sut.Reverse(sut.GetArray().Length + 1);
Assert.True(sut.GetArray().SequenceEqual(output));
}
[Fact]
public void Reverse_WithIndexLessThanZero_DoesNothing()
{
var input = new int[] { 1, 2 };
var output = new int[] { 1, 2 };
var sut = new TestReverser(1);
sut.SetArray(input);
sut.Reverse(-1);
Assert.True(sut.GetArray().SequenceEqual(output));
}
[Theory]
[InlineData(new int[] { 1 })]
[InlineData(new int[] { 1, 2 })]
[InlineData(new int[] { 1, 1 })]
public void IsArrayInAscendingOrder_WhenArrayElementsAreInNumericAscendingOrder_ReturnsTrue(int[] input)
{
var sut = new TestReverser(1);
sut.SetArray(input);
var result = sut.IsArrayInAscendingOrder();
Assert.True(result);
}
[Fact]
public void IsArrayInOrder_WhenArrayElementsAreNotInNumericAscendingOrder_ReturnsFalse()
{
var sut = new TestReverser(1);
sut.SetArray(new int[] { 2, 1 });
var result = sut.IsArrayInAscendingOrder();
Assert.False(result);
}
[Fact]
public void GetArrayString_ReturnsSpaceSeparatedElementsOfArrayInStringFormat()
{
var sut = new TestReverser(1);
sut.SetArray(new int[] { 1, 2 });
var result = sut.GetArrayString();
Assert.Equal(" 1 2 ", result);
}
}
}
@@ -0,0 +1,17 @@
namespace Reverse.Tests
{
internal class TestReverser : Reverser
{
public TestReverser(int arraySize) : base(arraySize) { }
public int[] GetArray()
{
return _array;
}
public void SetArray(int[] array)
{
_array = array;
}
}
}
+31
View File
@@ -0,0 +1,31 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.32002.261
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Reverse", "Reverse\Reverse.csproj", "{39463B63-6A71-4DCF-A4F2-FAA74FDEEC01}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Reverse.Tests", "Reverse.Tests\Reverse.Tests.csproj", "{96E824F8-0353-4FF2-9FEA-F850E2BE7312}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{39463B63-6A71-4DCF-A4F2-FAA74FDEEC01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{39463B63-6A71-4DCF-A4F2-FAA74FDEEC01}.Debug|Any CPU.Build.0 = Debug|Any CPU
{39463B63-6A71-4DCF-A4F2-FAA74FDEEC01}.Release|Any CPU.ActiveCfg = Release|Any CPU
{39463B63-6A71-4DCF-A4F2-FAA74FDEEC01}.Release|Any CPU.Build.0 = Release|Any CPU
{96E824F8-0353-4FF2-9FEA-F850E2BE7312}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{96E824F8-0353-4FF2-9FEA-F850E2BE7312}.Debug|Any CPU.Build.0 = Debug|Any CPU
{96E824F8-0353-4FF2-9FEA-F850E2BE7312}.Release|Any CPU.ActiveCfg = Release|Any CPU
{96E824F8-0353-4FF2-9FEA-F850E2BE7312}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1DCA2723-D126-4B37-A698-D40DA03643A9}
EndGlobalSection
EndGlobal
@@ -0,0 +1,130 @@
using System;
namespace Reverse
{
class Program
{
private static int arrayLength = 9;
static void Main(string[] args)
{
PrintTitle();
Console.Write("DO YOU WANT THE RULES? ");
var needRulesInput = Console.ReadLine();
Console.WriteLine();
if (string.Equals(needRulesInput, "YES", StringComparison.OrdinalIgnoreCase))
{
DisplayRules();
}
var tryAgain = string.Empty;
while (!string.Equals(tryAgain, "NO", StringComparison.OrdinalIgnoreCase))
{
var reverser = new Reverser(arrayLength);
Console.WriteLine("HERE WE GO ... THE LIST IS:");
PrintList(reverser.GetArrayString());
var arrayIsInAscendingOrder = false;
var numberOfMoves = 0;
while (arrayIsInAscendingOrder == false)
{
int index = ReadNextInput();
if (index == 0)
{
break;
}
reverser.Reverse(index);
PrintList(reverser.GetArrayString());
arrayIsInAscendingOrder = reverser.IsArrayInAscendingOrder();
numberOfMoves++;
}
if (arrayIsInAscendingOrder)
{
Console.WriteLine($"YOU WON IT IN {numberOfMoves} MOVES!!!");
}
Console.WriteLine();
Console.WriteLine();
Console.Write("TRY AGAIN (YES OR NO) ");
tryAgain = Console.ReadLine();
}
Console.WriteLine();
Console.WriteLine("OK HOPE YOU HAD FUN!!");
}
private static int ReadNextInput()
{
Console.Write("HOW MANY SHALL I REVERSE? ");
var input = ReadIntegerInput();
while (input > 9 || input < 0)
{
if (input > 9)
{
Console.WriteLine($"OOPS! TOO MANY! I CAN REVERSE AT MOST {arrayLength}");
}
if (input < 0)
{
Console.WriteLine($"OOPS! TOO FEW! I CAN REVERSE BETWEEN 1 AND {arrayLength}");
}
Console.Write("HOW MANY SHALL I REVERSE? ");
input = ReadIntegerInput();
}
return input;
}
private static int ReadIntegerInput()
{
var input = Console.ReadLine();
int.TryParse(input, out var index);
return index;
}
private static void PrintList(string list)
{
Console.WriteLine();
Console.WriteLine(list);
Console.WriteLine();
}
private static void PrintTitle()
{
Console.WriteLine("\t\t REVERSE");
Console.WriteLine(" CREATIVE COMPUTING MORRISTON, NEW JERSEY");
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("REVERSE -- A GAME OF SKILL");
Console.WriteLine();
}
private static void DisplayRules()
{
Console.WriteLine();
Console.WriteLine("THIS IS THE GAME OF 'REVERSE'. TO WIN, ALL YOU HAVE");
Console.WriteLine("TO DO IS ARRANGE A LIST OF NUMBERS (1 THOUGH 9 )");
Console.WriteLine("IN NUMERICAL ORDER FROM LEFT TO RIGHT. TO MOVE, YOU");
Console.WriteLine("TELL ME HOW MANY NUMBERS (COUNTING FROM THE LEFT) TO");
Console.WriteLine("REVERSE. FOR EXAMPLE, IF THE CURRENT LIST IS:");
Console.WriteLine();
Console.WriteLine("2 3 4 5 1 6 7 8 9");
Console.WriteLine();
Console.WriteLine("AND YOU REVERSE 4, THE RESULT WILL BE:");
Console.WriteLine();
Console.WriteLine("5 4 3 2 1 6 7 8 9");
Console.WriteLine();
Console.WriteLine("NOW IF YOU REVERSE 5, YOU WIN!");
Console.WriteLine();
Console.WriteLine("1 2 3 4 5 6 7 8 9");
Console.WriteLine();
Console.WriteLine("NO DOUBT YOU WILL LIKE THIS GAME, BUT ");
Console.WriteLine("IF YOU WANT TO QUIT, REVERSE 0 (ZERO)");
Console.WriteLine();
Console.WriteLine();
}
}
}
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
</Project>
@@ -0,0 +1,82 @@
using System;
using System.Text;
namespace Reverse
{
public class Reverser
{
protected int[] _array;
public Reverser(int arraySize)
{
_array = CreateRandomArray(arraySize);
}
public void Reverse(int index)
{
if (index > _array.Length)
{
return;
}
for (int i = 0; i < index / 2; i++)
{
int temp = _array[i];
int upperIndex = index - 1 - i;
_array[i] = _array[upperIndex];
_array[upperIndex] = temp;
}
}
public bool IsArrayInAscendingOrder()
{
for (int i = 1; i < _array.Length; i++)
{
if (_array[i] < _array[i - 1])
{
return false;
}
}
return true;
}
private int[] CreateRandomArray(int size)
{
if (size < 1)
{
throw new ArgumentOutOfRangeException(nameof(size), "Array size must be a positive integer");
}
var array = new int[size];
for (int i = 1; i <= size; i++)
{
array[i - 1] = i;
}
var rnd = new Random();
for (int i = size; i > 1;)
{
int k = rnd.Next(i);
--i;
int temp = array[i];
array[i] = array[k];
array[k] = temp;
}
return array;
}
public string GetArrayString()
{
var sb = new StringBuilder();
foreach (int i in _array)
{
sb.Append(" " + i + " ");
}
return sb.ToString();
}
}
}
+7
View File
@@ -1,3 +1,10 @@
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
Conversion to [Oracle Java](https://openjdk.java.net/)
Two versions of Roulette has been contributed. They are indicated within given sub-folders
- [oop](./oop) - Conversion by Andrew McGuinness (andrew@arobeia.co.uk)
- [iterative](./iterative) - Conversion by Thomas Kwashnak ([Github](https://github.com/LittleTealeaf)).
- Implements features from JDK 17.
- Does make use of some object oriented programming, but acts as a more iterative solution.
+277
View File
@@ -0,0 +1,277 @@
import java.io.InputStream;
import java.io.PrintStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Random;
import java.util.Scanner;
import java.util.Set;
public class Roulette {
private static Set<Integer> RED_NUMBERS;
static {
RED_NUMBERS = Set.of(1, 3, 5, 7, 9, 12, 14, 16, 18, 19, 21, 23, 25, 27, 30, 32, 34, 36);
}
private PrintStream out;
private Scanner scanner;
private int houseBalance, playerBalance;
private Random random;
public Roulette(PrintStream out, InputStream in) {
this.out = out;
this.scanner = new Scanner(in);
houseBalance = 100000;
playerBalance = 1000;
random = new Random();
}
public static void main(String[] args) {
new Roulette(System.out, System.in).play();
}
public void play() {
out.println(" ROULETTE");
out.println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
out.println("WELCOME TO THE ROULETTE TABLE\n");
out.print("DO YOU WANT INSTRUCTIONS? ");
if (scanner.nextLine().toLowerCase().charAt(0) != 'n') {
printInstructions();
}
do {
Bet[] bets = queryBets();
out.print("SPINNING...\n\n");
int result = random.nextInt(1, 39);
/*
Equivalent to following line
if(result == 37) {
out.println("00");
} else if(result == 38) {
out.println("0");
} else if(RED_NUMBERS.contains(result)) {
out.println(result + " RED");
} else {
out.println(result + " BLACK");
}
*/
out.println(switch (result) {
case 37 -> "00";
case 38 -> "0";
default -> result + (RED_NUMBERS.contains(result) ? " RED" : " BLACK");
});
betResults(bets, result);
out.println();
out.println("TOTALS:\tME\t\tYOU");
out.format("\t\t%5d\t%d\n", houseBalance, playerBalance);
} while (playAgain());
if (playerBalance <= 0) {
out.println("THANKS FOR YOUR MONEY\nI'LL USE IT TO BUY A SOLID GOLD ROULETTE WHEEL");
} else {
printCheck();
}
out.println("COME BACK SOON!");
}
public void printInstructions() {
out.println();
out.println("THIS IS THE BETTING LAYOUT");
out.println(" (*=RED)");
out.println();
out.println(" 1* 2 3*");
out.println(" 4 5* 6 ");
out.println(" 7* 8 9*");
out.println("10 11 12*");
out.println("---------------");
out.println("13 14* 15 ");
out.println("16* 17 18*");
out.println("19* 20 21*");
out.println("22 23* 24 ");
out.println("---------------");
out.println("25* 26 27*");
out.println("28 29 30*");
out.println("31 32* 33 ");
out.println("34* 35 36*");
out.println("---------------");
out.println(" 00 0 ");
out.println();
out.println("TYPES OF BETS");
out.println();
out.println("THE NUMBERS 1 TO 36 SIGNIFY A STRAIGHT BET");
out.println("ON THAT NUMBER.");
out.println("THESE PAY OFF 35:1");
out.println();
out.println("THE 2:1 BETS ARE:");
out.println(" 37) 1-12 40) FIRST COLUMN");
out.println(" 38) 13-24 41) SECOND COLUMN");
out.println(" 39) 25-36 42) THIRD COLUMN");
out.println();
out.println("THE EVEN MONEY BETS ARE:");
out.println(" 43) 1-18 46) ODD");
out.println(" 44) 19-36 47) RED");
out.println(" 45) EVEN 48) BLACK");
out.println();
out.println(" 49)0 AND 50)00 PAY OFF 35:1");
out.println(" NOTE: 0 AND 00 DO NOT COUNT UNDER ANY");
out.println(" BETS EXCEPT THEIR OWN.");
out.println();
out.println("WHEN I ASK FOR EACH BET, TYPE THE NUMBER");
out.println("AND THE AMOUNT, SEPARATED BY A COMMA.");
out.println("FOR EXAMPLE: TO BET $500 ON BLACK, TYPE 48,500");
out.println("WHEN I ASK FOR A BET.");
out.println();
out.println("THE MINIMUM BET IS $5, THE MAXIMUM IS $500.");
}
private Bet[] queryBets() {
int numBets = -1;
while (numBets < 1) {
out.print("HOW MANY BETS? ");
try {
numBets = Integer.parseInt(scanner.nextLine());
} catch (NumberFormatException ignored) {
}
}
Bet[] bets = new Bet[numBets];
for (int i = 0; i < numBets; i++) {
while (bets[i] == null) {
try {
out.print("NUMBER" + (i + 1) + "? ");
String[] values = scanner.nextLine().split(",");
int betNumber = Integer.parseInt(values[0]);
int betValue = Integer.parseInt(values[1]);
for (int j = 0; j < i; j++) {
if (bets[j].num == betNumber) {
out.println("YOU MADE THAT BET ONCE ALREADY,DUM-DUM");
betNumber = -1; //Since -1 is out of the range, this will throw it out at the end
}
}
if (betNumber > 0 && betNumber <= 50 && betValue >= 5 && betValue <= 500) {
bets[i] = new Bet(betNumber,betValue);
}
} catch (Exception ignored) {
}
}
}
return bets;
}
private void betResults(Bet[] bets, int num) {
for (int i = 0; i < bets.length; i++) {
Bet bet = bets[i];
/*
Using a switch statement of ternary operators that check if a certain condition is met based on the bet value
Returns the coefficient that the bet amount should be multiplied by to get the resulting value
*/
int coefficient = switch (bet.num) {
case 37 -> (num <= 12) ? 2 : -1;
case 38 -> (num > 12 && num <= 24) ? 2 : -1;
case 39 -> (num > 24 && num < 37) ? 2 : -1;
case 40 -> (num < 37 && num % 3 == 1) ? 2 : -1;
case 41 -> (num < 37 && num % 3 == 2) ? 2 : -1;
case 42 -> (num < 37 && num % 3 == 0) ? 2 : -1;
case 43 -> (num <= 18) ? 1 : -1;
case 44 -> (num > 18 && num <= 36) ? 1 : -1;
case 45 -> (num % 2 == 0) ? 1 : -1;
case 46 -> (num % 2 == 1) ? 1 : -1;
case 47 -> RED_NUMBERS.contains(num) ? 1 : -1;
case 48 -> !RED_NUMBERS.contains(num) ? 1 : -1;
case 49 -> (num == 37) ? 35 : -1;
case 50 -> (num == 38) ? 35 : -1;
default -> (bet.num < 49 && bet.num == num) ? 35 : -1;
};
int betResult = bet.amount * coefficient;
if (betResult < 0) {
out.println("YOU LOSE " + -betResult + " DOLLARS ON BET " + (i + 1));
} else {
out.println("YOU WIN " + betResult + " DOLLARS ON BET " + (i + 1));
}
playerBalance += betResult;
houseBalance -= betResult;
}
}
private boolean playAgain() {
if (playerBalance <= 0) {
out.println("OOPS! YOU JUST SPENT YOUR LAST DOLLAR!");
return false;
} else if (houseBalance <= 0) {
out.println("YOU BROKE THE HOUSE!");
playerBalance = 101000;
houseBalance = 0;
return false;
} else {
out.println("PLAY AGAIN?");
return scanner.nextLine().toLowerCase().charAt(0) == 'y';
}
}
private void printCheck() {
out.print("TO WHOM SHALL I MAKE THE CHECK? ");
String name = scanner.nextLine();
out.println();
for (int i = 0; i < 72; i++) {
out.print("-");
}
out.println();
for (int i = 0; i < 50; i++) {
out.print(" ");
}
out.println("CHECK NO. " + random.nextInt(0, 100));
for (int i = 0; i < 40; i++) {
out.print(" ");
}
out.println(LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE));
out.println();
out.println("PAY TO THE ORDER OF -----" + name + "----- $" + (playerBalance));
out.println();
for (int i = 0; i < 40; i++) {
out.print(" ");
}
out.println("THE MEMORY BANK OF NEW YORK");
for (int i = 0; i < 40; i++) {
out.print(" ");
}
out.println("THE COMPUTER");
for (int i = 0; i < 40; i++) {
out.print(" ");
}
out.println("----------X-----");
for (int i = 0; i < 72; i++) {
out.print("-");
}
out.println();
}
public class Bet {
final int num, amount;
public Bet(int num, int amount) {
this.num = num;
this.amount = amount;
}
}
}
+65
View File
@@ -0,0 +1,65 @@
/* A bet has a target (the code entered, which is 1-36, or special values for
* the various groups, zero and double-zero), and an amount in dollars
*/
public class Bet {
public int target;
public int amount;
/* bet on a target, of an amount */
public Bet(int on, int of) {
target = on; amount = of;
}
/* check if this is a valid bet - on a real target and of a valid amount */
public boolean isValid() {
return ((target > 0) && (target <= 50) &&
(amount >= 5) && (amount <= 500));
}
/* utility to return either the odds amount in the case of a win, or zero for a loss */
private int m(boolean isWon, int odds) {
return isWon? odds: 0;
}
/* look at the wheel to see if this bet won.
* returns 0 if it didn't, or the odds if it did
*/
public int winsOn(Wheel w) {
if (target < 37) {
// A number bet 1-36 wins at odds of 35 if it is the exact number
return m(w.isNumber() && (w.number() == target), 35);
} else
switch (target) {
case 37: // 1-12, odds of 2
return m(w.isNumber() && (w.number() <= 12), 2);
case 38: // 13-24, odds of 2
return m(w.isNumber() && (w.number() > 12) && (w.number() <= 24), 2);
case 39: // 25-36, odds of 2
return m(w.isNumber() && (w.number() > 24), 2);
case 40: // Column 1, odds of 2
return m(w.isNumber() && ((w.number() % 3) == 1), 2);
case 41: // Column 2, odds of 2
return m(w.isNumber() && ((w.number() % 3) == 2), 2);
case 42: // Column 3, odds of 2
return m(w.isNumber() && ((w.number() % 3) == 0), 2);
case 43: // 1-18, odds of 1
return m(w.isNumber() && (w.number() <= 18), 1);
case 44: // 19-36, odds of 1
return m(w.isNumber() && (w.number() > 18), 1);
case 45: // even, odds of 1
return m(w.isNumber() && ((w.number() %2) == 0), 1);
case 46: // odd, odds of 1
return m(w.isNumber() && ((w.number() %2) == 1), 1);
case 47: // red, odds of 1
return m(w.isNumber() && (w.color() == Wheel.BLACK), 1);
case 48: // black, odds of 1
return m(w.isNumber() && (w.color() == Wheel.RED), 1);
case 49: // single zero, odds of 35
return m(w.value().equals("0"), 35);
case 50: // double zero, odds of 35
return m(w.value().equals("00"), 35);
}
throw new RuntimeException("Program Error - invalid bet");
}
}
+234
View File
@@ -0,0 +1,234 @@
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Random;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
public class Roulette {
public static void main(String args[]) throws Exception {
Roulette r = new Roulette();
r.play();
}
private BufferedReader reader;
private PrintStream writer;
private int house; // how much money does the house have
private int player; // how much money does the player have
private Wheel wheel = new Wheel();
public Roulette() {
reader = new BufferedReader(new InputStreamReader(System.in));
writer = System.out;
house = 100000;
player = 1000;
}
// for a test / cheat mode -- set the random number generator to a known value
private void setSeed(long l) {
wheel.setSeed(l);
}
public void play() {
try {
intro();
writer.println("WELCOME TO THE ROULETTE TABLE\n" +
"DO YOU WANT INSTRUCTIONS");
String instr = reader.readLine();
if (!instr.toUpperCase().startsWith("N"))
instructions();
while (betAndSpin()) { // returns true if the game is to continue
}
if (player <= 0) {
// player ran out of money
writer.println("THANKS FOR YOUR MONEY.\nI'LL USE IT TO BUY A SOLID GOLD ROULETTE WHEEL");
} else {
// player has money -- print them a check
writer.println("TO WHOM SHALL I MAKE THE CHECK");
String payee = reader.readLine();
writer.println("-".repeat(72));
tab(50); writer.println("CHECK NO. " + (new Random().nextInt(100) + 1));
writer.println();
tab(40); writer.println(LocalDate.now().format(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG)));
writer.println("\n\nPAY TO THE ORDER OF-----" + payee + "-----$ " + player);
writer.print("\n\n");
tab(10); writer.println("THE MEMORY BANK OF NEW YORK\n");
tab(40); writer.println("THE COMPUTER");
tab(40); writer.println("----------X-----\n");
writer.println("-".repeat(72));
writer.println("COME BACK SOON!\n");
}
}
catch (IOException e) {
// this should not happen
System.err.println("System error:\n" + e);
}
}
/* Write the starting introduction */
private void intro() throws IOException {
tab(32); writer.println("ROULETTE");
tab(15); writer.println("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n");
}
/* Display the game instructions */
private void instructions() {
String[] instLines = new String[] {
"THIS IS THE BETTING LAYOUT",
" (*=RED)",
"" ,
" 1* 2 3*",
" 4 5* 6 ",
" 7* 8 9*",
"10 11 12*",
"---------------",
"13 14* 15 ",
"16* 17 18*",
"19* 20 21*",
"22 23* 24 ",
"---------------",
"25* 26 27*",
"28 29 30*",
"31 32* 33 ",
"34* 35 36*",
"---------------",
" 00 0 ",
"" ,
"TYPES OF BETS",
"" ,
"THE NUMBERS 1 TO 36 SIGNIFY A STRAIGHT BET",
"ON THAT NUMBER.",
"THESE PAY OFF 35:1",
"" ,
"THE 2:1 BETS ARE:",
" 37) 1-12 40) FIRST COLUMN",
" 38) 13-24 41) SECOND COLUMN",
" 39) 25-36 42) THIRD COLUMN",
"" ,
"THE EVEN MONEY BETS ARE:",
" 43) 1-18 46) ODD",
" 44) 19-36 47) RED",
" 45) EVEN 48) BLACK",
"",
" 49)0 AND 50)00 PAY OFF 35:1",
" NOTE: 0 AND 00 DO NOT COUNT UNDER ANY",
" BETS EXCEPT THEIR OWN.",
"",
"WHEN I ASK FOR EACH BET, TYPE THE NUMBER",
"AND THE AMOUNT, SEPARATED BY A COMMA.",
"FOR EXAMPLE: TO BET $500 ON BLACK, TYPE 48,500",
"WHEN I ASK FOR A BET.",
"",
"THE MINIMUM BET IS $5, THE MAXIMUM IS $500.",
"" };
writer.println(String.join("\n", instLines));
}
/* Take a set of bets from the player, then spin the wheel and work out the winnings *
* This returns true if the game is to continue afterwards
*/
private boolean betAndSpin() throws IOException {
int betCount = 0;
while (betCount == 0) { // keep asking how many bets until we get a good answer
try {
writer.println("HOW MANY BETS");
String howMany = reader.readLine();
betCount = Integer.parseInt(howMany.strip());
if ((betCount < 1) || (betCount > 100)) betCount = 0; // bad -- set zero and ask again
}
catch (NumberFormatException e) {
// this happens if the input is not a number
writer.println("INPUT ERROR");
}
}
HashSet<Integer> betsMade = new HashSet<>(); // Bet targets already made, so we can spot repeats
ArrayList<Bet> bets = new ArrayList<>(); // All the bets for this round
while (bets.size() < betCount) {
Bet bet = new Bet(0, 0); // an invalid bet to hold the place
while (!bet.isValid()) { // keep asking until it is valid
try {
writer.println("NUMBER " + (bets.size() + 1));
String fields[] = reader.readLine().split(",");
if (fields.length == 2) {
bet = new Bet(Integer.parseInt(fields[0].strip()),
Integer.parseInt(fields[1].strip()));
}
}
catch (NumberFormatException e) {
writer.println("INPUT ERROR");
}
}
// Check if there is already a bet on the same target
if (betsMade.contains(bet.target)) {
writer.println("YOU MADE THAT BET ONCE ALREADY,DUM-DUM");
} else {
betsMade.add(bet.target); // note this target has now been bet on
bets.add(bet);
}
}
writer.println("SPINNING\n\n");
wheel.spin(); // this deliberately takes some random amount of time
writer.println(wheel.value());
// go through the bets, and evaluate each one
int betNumber = 1;
for (Bet b : bets) {
int multiplier = b.winsOn(wheel);
if (multiplier == 0) {
// lost the amount of the bet
writer.println("YOU LOSE " + b.amount + " DOLLARS ON BET " + betNumber);
house += b.amount;
player -= b.amount;
} else {
// won the amount of the bet, multiplied by the odds
int winnings = b.amount * multiplier;
writer.println("YOU WIN " + winnings + " DOLLARS ON BET " + betNumber);
house -= winnings;
player += winnings;
}
++betNumber;
}
writer.println("\nTOTALS:\tME\tYOU\n\t" + house + "\t" + player);
if (player <= 0) {
writer.println("OOPS! YOU JUST SPENT YOUR LAST DOLLAR");
return false; // do not repeat since the player has no more money
}
if (house <= 0) {
writer.println("YOU BROKE THE HOUSE!");
player = 101000; // can't win more than the house started with
return false; // do not repeat since the house has no more money
}
// player still has money, and the house still has money, so ask the player
// if they want to continue
writer.println("AGAIN");
String doContinue = reader.readLine();
// repeat if the answer was not "n" or "no"
return (!doContinue.toUpperCase().startsWith("N"));
}
// utility to print n spaces for formatting
private void tab(int n) {
writer.print(" ".repeat(n));
}
}
+69
View File
@@ -0,0 +1,69 @@
import java.util.Arrays;
import java.util.HashSet;
import java.util.Random;
// The roulette wheel
public class Wheel {
// List the numbers which are black
private HashSet<Integer> black = new HashSet<>(Arrays.asList(new Integer[] { 1,3,5,7,9,12,14,16,18,19,21,23,25,27,30,32,34,36 }));
private Random random = new Random();
private int pocket = 38;
public static final int ZERO=0;
public static final int BLACK=1;
public static final int RED=2;
// Set up a wheel. You call "spin", and then can check the result.
public Wheel() {
}
// Cheat / test mode
void setSeed(long l) {
random.setSeed(l);
}
// Spin the wheel onto a new random value.
public void spin() {
// keep spinning for a while
do {
try {
// 1 second delay. Where it stops, nobody knows
Thread.sleep(1000);
}
catch (InterruptedException e) {}
pocket = random.nextInt(38) + 1;
} while (random.nextInt(4) > 0); // keep spinning until it stops
}
// The string representation of the number; 1-36, 0, or 00
public String value() {
if (pocket == 37) return "0";
else if (pocket == 38) return "00";
else return String.valueOf(pocket);
}
// True if either 0 or 00 is hit
public boolean zero() {
return (pocket > 36);
}
// True if anything other than 0 or 00 is hit
public boolean isNumber() {
return (pocket < 37);
}
// The number rolled
public int number() {
if (zero()) return 0;
else return pocket;
}
// Either ZERO, BLACK, or RED
public int color() {
if (zero()) return ZERO;
else if (black.contains(pocket)) return BLACK;
else return RED;
}
}
+12
View File
@@ -1,3 +1,15 @@
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
Conversion to [Perl](https://www.perl.org/)
This conversion consists of three files in `75_Roulette/perl/`:
- `roulette.pl` is the port of the BASIC to Perl;
- `roulette-test.t` is a Perl test for correctness of display and payout;
- `make-roulette-test.pl` generates roulette-test.t from roulette.bas.
The ported version of the game numbers the slots from 0 rather than 1, and uses a dispatch table to figure out the payout.
The Perl test loads `roulette.pl` and verifies the Perl slot display and payout logic against the BASIC for all combinations of slots and bets. If any tests fail that fact will be noted at the end of the output.
The test code is generated by reading the BASIC, retaining only the slot display and payout logic (based on line numbers), and wrapping this in code that generates all combinations of bet and spin result. The result is run, and the result is captured and parsed to produce `roulette-test.t`. `make-roulette-test.pl` has some command-line options that may be of interest. `--help` will display the documentation.
+263
View File
@@ -0,0 +1,263 @@
#!/usr/bin/env perl
use 5.014; # For s///r
use strict;
use warnings;
use File::Temp;
use Getopt::Long 2.33 qw{ :config auto_version };
use IPC::Cmd qw{ can_run }; # Core as of Perl 5.9.5.
use Pod::Usage;
our $VERSION = '0.000_01';
my %opt = (
program => find_basic(),
output => make_default_output(),
);
GetOptions( \%opt,
qw{ output=s program=s },
help => sub { pod2usage( { -verbose => 2 } ) },
) or pod2usage( { -verbose => 0 } );
die "No default BASIC found; you must specify --program\n"
unless defined $opt{program};
my $game_dir = ( File::Spec->splitdir( $0 ) )[0];
my $basic_file = File::Spec->catfile( $game_dir, 'roulette.bas' );
open my $basic_handle, '<', $basic_file
or die "Unable to open $basic_file: $!\n";
my $munged = File::Temp->new();
print { $munged } <<'EOD';
1000 Y=50
1010 DIM B(100),C(100),T(100)
1090 FOR S=1 TO 38
1095 PRINT "SPIN ";S
1100 FOR C=1 TO Y
1110 B(C)=1
1120 T(C)=C
1130 NEXT C
EOD
transcribe( $basic_file, $basic_handle, $munged, 1860, 2810 );
transcribe( $basic_file, $basic_handle, $munged, 2950 );
say { $munged } '4000 NEXT S';
$munged->flush();
if ( $opt{output} ne '-' ) {
my $dir = ( File::Spec->splitpath( $0 ) )[1];
my $fn = File::Spec->rel2abs( $opt{output}, $dir );
$fn = File::Spec->abs2rel( $fn );
open my $fh, '>', $fn
or die "Unable to open $fn: $!\n";
warn "Writing $fn\n";
select $fh;
}
print <<'EOD';
package main;
use 5.010;
use strict;
use warnings;
use File::Spec;
use Test::More 0.88; # Because of done_testing();
EOD
print <<"EOD";
# NOTE: This file is generated by $0.
# Any edits made to it will be lost the next time it is regenerated.
# Caveat coder.
EOD
print <<'EOD';
my $dir = ( File::Spec->splitpath( $0 ) )[1];
my $script = File::Spec->catfile( $dir, 'roulette.pl' );
{
# Modern Perls do not have . in @INC, but we need it there to load a
# relative path.
local @INC = ( File::Spec->curdir(), @INC );
require $script; # Load game as module
}
EOD
my $spin;
my $name;
foreach ( `$opt{program} @{[ $munged->filename() ]}` ) {
s/\N{U+1D}/ /smxg; # Artifact of the BASIC I'm using.
s/ \s+ \z //smx;
s/ \A \s+ //smx;
if ( $_ eq '' ) {
# Ignore empty lines.
} elsif ( m/ \A SPIN \s* ( [0-9]+ ) /smx ) {
$spin = $1 - 1; # BASIC is 1-based, but Perl is 0-based
} elsif ( m/ \A YOU \s+ WIN \s* ( [0-9]+ ) \s*
DOLLARS \s+ ON \s+ BET \s* ( [0-9]+ ) /smx ) {
say "is payout( $spin, $2 ), $1, 'Spin $spin ($name), bet $2 pays $1';";
} elsif ( m/ \A YOU \s+ LOSE \s* ( [0-9]+ ) \s*
DOLLARS \s+ ON \s+ BET \s* ( [0-9]+ ) /smx ) {
say "is payout( $spin, $2 ), -$1, 'Spin $spin ($name), bet $2 pays -$1';";
} elsif ( m/ \A \s* ( [0-9]+ ) (?: \s* ( [[:alpha:]]+ ) )? \z /smx ) {
$name = $2 ? sprintf( '%d %s', $1, ucfirst lc $2 ) : $1;
say "is format_spin( $spin ), '$name', 'Spin $spin is $name';";
} else {
die "Unexpected input $_";
}
}
print <<'EOD';
done_testing;
1;
# ex: set textwidth=72 :
EOD
sub find_basic {
# yabasic seems not to work
foreach my $prog ( qw{ basic cbmbasic } ) {
return $prog if can_run( $prog )
}
return undef;
}
sub make_default_output {
( my $rslt = $0 ) =~ s/ [.] pl \z /.t/smx;
$rslt =~ s/ .* \b make- //smx;
return $rslt;
}
sub transcribe {
my ( $in_file, $in_handle, $out_handle, $first_line, $last_line ) = @_;
$last_line //= $first_line;
while ( <$in_handle> ) {
m/ \A \s* ( [0-9]+ )+ \s /smx
or next;
$1 < $first_line
and next;
say { $out_handle } sprintf '%04d REM BEGIN VERBATIM FROM %s',
$first_line - 10, $in_file;
print { $out_handle } $_;
last;
}
while ( <$in_handle> ) {
m/ \A \s* ( [0-9]+ )+ \s /smx
and $1 > $last_line
and last;
print { $out_handle } $_;
}
say { $out_handle } sprintf '%04d REM END VERBATIM FROM %s',
$last_line + 10, $in_file;
return;
}
__END__
=head1 TITLE
make-roulette-test.pl - Generate the tests for 75_Roulette/perl/roulette.pl
=head1 SYNOPSIS
perl 75_Roulette/perl/make-roulette-test.pl
perl 75_Roulette/perl/make-roulette-test.pl --program mybasic
perl 75_Roulette/perl/make-roulette-test.pl --help
perl 75_Roulette/perl/make-roulette-test.pl --version
=head1 OPTIONS
<<< replace boiler plate >>>
=head2 --help
This option displays the documentation for this script. The script then
exits.
=head2 --output
--output fubar.t
This option specifies the output file. This needs to be in the same
directory as F<roulette.pl>, and defaults to that directory. A single
dash (C<'-'>) is special-cased to send the output to standard out.
The default is C<--output=test-roulette.t>.
=head2 --program
--program my_basic
This option specifies the name of your BASIC interpreter. This must be
the name of an executable file in your PATH (aliases do not work).
The default is the first-found in the list C<qw{ basic cbmbasic }>.
=head2 --version
This option displays the version of this script. The script then exits.
=head1 DETAILS
This Perl script generates F<roulette-test.t>, which tests
F<roulette.pl>. The latter is expected to be written as a modulino.
This script assumes that:
=over
=item * it is in the same directory as F<roulette.pl>;
=item * F<roulette.bas> is in the first-level subdirectory under the current directory;
=back
The generated test assumes that it is in the same directory as
F<roulette.pl>.
This script works by abstracting the internals of F<roulette.bas> and
wrapping them in a loop that generates all possible spins, and places
all possible bets on each spin. The generated BASIC is written to a
temporary file, and executed by a BASIC interpreter. The output is
parsed and used to generate the output.
Obviously there is some ad-hocery going on, and this script has only
been tested under C<cbmbasic>, which was what I had on hand.
B<Caveat:> the abstraction process is driven by BASIC line numbers. Any
change of these puts the ad-hocery at risk.
=head1 AUTHOR
Thomas R. Wyant, III F<wyant at cpan dot org>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2022 by Thomas R. Wyant, III
This program is free software; you can redistribute it and/or modify it
under the same terms as Perl 5.10.0. For more details, see the Artistic
License 1.0 at
L<https://www.perlfoundation.org/artistic-license-10.html>, and/or the
Gnu GPL at L<http://www.gnu.org/licenses/old-licenses/gpl-1.0.txt>.
This program is distributed in the hope that it will be useful, but
without any warranty; without even the implied warranty of
merchantability or fitness for a particular purpose.
=cut
# ex: set textwidth=72 :
File diff suppressed because it is too large Load Diff
+319
View File
@@ -0,0 +1,319 @@
#!/usr/bin/env perl
use 5.010; # To get 'state' and 'say'
use strict; # Require explicit declaration of variables
use warnings; # Enable optional compiler warnings
use English; # Use more friendly names for Perl's magic variables
use POSIX qw{ strftime }; # Time formatting
use Term::ReadLine; # Prompt and return user input
our $VERSION = '0.000_01';
# A main() function is not usual in Perl scripts. I have installed one
# here to make the script into a "modulino." The next line executes
# main() if and only if caller() returns false. It will do this if we
# were loaded by another Perl script but not otherwise. This was done so
# I could test the payout and spin formatting logic.
main() unless caller;
sub main {
print <<'EOD';
ROULETTE
Creative Computing Morristown, New Jersey
Welcome to the roulette table.
EOD
if ( get_yes_no( 'Do you want instructions' ) ) {
print <<'EOD';
This is the betting layout
(*=red)
1* 2 3*
4 5* 6
7* 8 9*
10 11 12*
---------------
13 14* 15
16* 17 18*
19* 20 21*
22 23* 24
---------------
25* 26 27*
28 29 30*
31 32* 33
34* 35 36*
---------------
00 0
Types of bets:
The numbers 1 to 36 signify a straight bet
on that number.
These pay off 35:1
The 2:1 bets are:
37) 1-12 40) first column
38) 13-24 41) second column
39) 25-36 42) third column
The even money bets are:
43) 1-18 46) odd
44) 19-36 47) red
45) even 48) black
49) 0 and 50) 00 pay off 35:1
Note: 0 and 00 do not count under any
bets except their own.
When I ask for each bet, type the number
and the amount, separated by a comma.
For example: to bet $500 on black, type 48,500
when I ask for a bet.
The minimum bet is $5, the maximum is $500.
EOD
}
my $P = 1000;
my $D = 100000.;
while ( 1 ) { # Iterate indefinitely
my $Y = get_input( 'How many bets? ',
sub { m/ \A [0-9]+ \z /smx && $ARG > 0 && $ARG <= 50 },
"Please enter a positive integer no greater than 50\n",
);
my @B;
my @T;
foreach my $C ( 1 .. $Y ) {
my ( $X, $Z ) = split qr< , >smx, get_input(
"Number $C: ",
sub { m/ \A ( [0-9]+ ) , ( [0-9]+ ) \z /smx
&& $1 > 0 && $1 <= 50 && $2 >= 5 && $2 <= 500 },
"Please enter two comma-separated positive numbers\n",
);
if ( $B[$X] ) {
say 'You made that bet once already, dum-dum.';
redo;
}
$B[$X] = $Z; # BASIC does $B[$C] = $Z
$T[$C] = $X;
}
print <<'EOD';
Spinning ...
EOD
my $S = int rand 38; # Zero-based, versus 1-based in BASIC
say format_spin( $S );
say '';
foreach my $C ( 1 .. $Y ) {
my $X = $T[$C];
my $payout = payout( $S, $X ) * $B[$X];
$D -= $payout;
$P += $payout;
if ( $payout > 0 ) {
say "You win $payout dollars on bet $C";
} else {
$payout = -$payout;
say "You lose $payout dollars on bet $C";
}
}
say "Totals\tMe\tYou";
say "\t$D\t$P";
say '';
last unless get_yes_no( 'Again' );
}
say '';
if ( $P > 0 ) {
my $B = get_input(
'To whom shall I make out the check? ',
);
my $check_number = 1000 + int rand 9000;
my $todays_date = strftime( '%B %d, %Y', localtime );
print <<"EOD";
------------------------------------------------------------ Check number $check_number
$todays_date
Pay to the order of ------ $B ----- \$$P
The Memory Bank of New York
The Computer
---------X-----
-------------------------------------------------------------------------------
Come back soon!
EOD
} else {
print <<'EOD';
Thanks for your money.
I'll use it to buy a solid gold roulette wheel
EOD
}
}
{
# Define the kind of each possible spin. 0 is '0' or '00', 1 is
# black, and 2 is red. We assign the values in a BEGIN block because
# execution never actually reaches this point in the script.
my @kind;
BEGIN {
@kind = ( 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 1,
2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 0,
0 );
}
# Convert the spin (0-37) to its name on the wheel.
sub format_spin {
my ( $number ) = @_;
state $format = [
sub { '0' x ( $_[0] - 35 ) },
sub { sprintf '%s Black', $_[0] + 1 },
sub { sprintf '%s Red', $_[0] + 1 },
];
return $format->[$kind[$number]]( $number );
}
# Compute the payout given the spin (0-37) and the bet (1-50).
sub payout {
my ( $number, $bet ) = @_;
# We compute the payout on '0' and '00' directly, since under
# our rules they are only eligible for the 35-to-1 bet.
$kind[$number]
or return $number == $bet - 49 + 36 ? 35 : -1;
--$bet; # #bet is 1-based coming in
# Dispatch table for computing the payout for spins 0-36.
state $payout = [
( sub { $_[0] == $_[1] ? 35 : -1 } ) x 36,
( sub { int( $_[0] / 12 ) == $_[1] - 36 ? 2 : -1 } ) x 3,
( sub { $_[0] % 3 == $_[1] - 39 ? 2 : -1 } ) x 3,
( sub { int( $_[0] / 18 ) == $_[1] - 42 ? 1 : -1 } ) x 2,
( sub { $_[0] % 2 == 45 - $_[1] ? 1 : -1 } ) x 2,
( sub { $kind[$_[0]] == 48 - $_[1] ? 1 : -1 } ) x 2,
( sub { -1 } ) x 2, # Bet on '0' or '00' loses
];
return $payout->[$bet]->( $number, $bet );
}
}
# Get input from the user. The arguments are:
# * The prompt
# * A reference to validation code. This code receives the response in
# $ARG and returns true for a valid response.
# * A warning to print if the response is not valid. This must end in a
# return.
# The first valid response is returned. An end-of-file terminates the
# script.
sub get_input {
my ( $prompt, $validate, $warning ) = @ARG;
# If no validator is passed, default to one that always returns
# true.
$validate ||= sub { 1 };
# Create the readline object. The 'state' causes the variable to be
# initialized only once, no matter how many times this subroutine is
# called. The do { ... } is a compound statement used because we
# need to tweak the created object before we store it.
state $term = do {
my $obj = Term::ReadLine->new( 'reverse' );
$obj->ornaments( 0 );
$obj;
};
while ( 1 ) { # Iterate indefinitely
# Read the input into the topic variable, localized to prevent
# Spooky Action at a Distance. We exit on undef, which signals
# end-of-file.
exit unless defined( local $ARG = $term->readline( $prompt ) );
# Return the input if it is valid.
return $ARG if $validate->();
# Issue the warning, and go around the merry-go-round again.
warn $warning;
}
}
# Get a yes-or-no answer. The argument is the prompt, which will have
# '? [y/n]: ' appended. The donkey work is done by get_input(), which is
# requested to validate the response as beginning with 'y' or 'n',
# case-insensitive. The return is a true value for 'y' and a false value
# for 'n'.
sub get_yes_no {
my ( $prompt ) = @ARG;
state $map_answer = {
n => 0,
y => 1,
};
my $resp = lc get_input(
"$prompt? [y/n]: ",
sub { m/ \A [yn] /smxi },
"Please respond 'y' or 'n'\n",
);
return $map_answer->{ substr $resp, 0, 1 };
}
__END__
=head1 TITLE
roulette - Play the game 'Roulette' from Basic Computer Games
=head1 SYNOPSIS
roulette.pl
=head1 DETAILS
This Perl script is a port of roulette, which is the 75th
entry in Basic Computer Games.
The main internal changes are converting the roulette slot numbering to
0-based and replacing most of the payout logic with a dispatch table.
These changes were tested for correctness against the original BASIC.
=head1 PORTED BY
Thomas R. Wyant, III F<wyant at cpan dot org>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2022 by Thomas R. Wyant, III
This program is free software; you can redistribute it and/or modify it
under the same terms as Perl 5.10.0. For more details, see the Artistic
License 1.0 at
L<https://www.perlfoundation.org/artistic-license-10.html>, and/or the
Gnu GPL at L<http://www.gnu.org/licenses/old-licenses/gpl-1.0.txt>.
This program is distributed in the hope that it will be useful, but
without any warranty; without even the implied warranty of
merchantability or fitness for a particular purpose.
=cut
# ex: set expandtab tabstop=4 textwidth=72 :
+213
View File
@@ -0,0 +1,213 @@
from datetime import date
import random
global RED_NUMBERS
RED_NUMBERS = [1, 3, 5, 7, 9, 12, 14, 16, 18, 19, 21, 23, 25, 27, 30, 32, 34, 36]
def print_instructions():
print("""
THIS IS THE BETTING LAYOUT
(*=RED)
1* 2 3*
4 5* 6
7* 8 9*
10 11 12*
---------------
13 14* 15
16* 17 18*
19* 20 21*
22 23* 24
---------------
25* 26 27*
28 29 30*
31 32* 33
34* 35 36*
---------------
00 0
TYPES OF BETS
THE NUMBERS 1 TO 36 SIGNIFY A STRAIGHT BET
ON THAT NUMBER.
THESE PAY OFF 35:1
THE 2:1 BETS ARE:
37) 1-12 40) FIRST COLUMN
38) 13-24 41) SECOND COLUMN
39) 25-36 42) THIRD COLUMN
THE EVEN MONEY BETS ARE:
43) 1-18 46) ODD
44) 19-36 47) RED
45) EVEN 48) BLACK
49)0 AND 50)00 PAY OFF 35:1
NOTE: 0 AND 00 DO NOT COUNT UNDER ANY
BETS EXCEPT THEIR OWN.
WHEN I ASK FOR EACH BET, TYPE THE NUMBER
AND THE AMOUNT, SEPARATED BY A COMMA.
FOR EXAMPLE: TO BET $500 ON BLACK, TYPE 48,500
WHEN I ASK FOR A BET.
THE MINIMUM BET IS $5, THE MAXIMUM IS $500.
""")
def query_bets():
"""Queries the user to input their bets"""
betCount = -1
while betCount <= 0:
try:
betCount = int(input("HOW MANY BETS? "))
except:
...
bet_IDs = [-1] * betCount
bet_Values = [0] * betCount
for i in range(betCount):
while(bet_IDs[i] == -1):
try:
inString = input("NUMBER " + str(i + 1) + "? ").split(',')
id,val = int(inString[0]),int(inString[1])
# check other bet_IDs
for j in range(i):
if id != -1 and bet_IDs[j] == id:
id = -1
print("YOU ALREADY MADE THAT BET ONCE, DUM-DUM")
break
if id > 0 and id <= 50 and val >= 5 and val <= 500:
bet_IDs[i] = id
bet_Values[i] = val
except:
...
return bet_IDs,bet_Values
def bet_results(bet_IDs,bet_Values,result):
"""Computes the results, prints them, and returns the total net winnings"""
total_winnings = 0
def get_modifier(id,num):
if id == 37 and num <= 12:
return 2
elif id == 38 and num > 12 and num <= 24:
return 2
elif id == 39 and num > 24 and num < 37:
return 2
elif id == 40 and num < 37 and num % 3 == 1:
return 2
elif id == 41 and num < 37 and num % 3 == 2:
return 2
elif id == 42 and num < 37 and num % 3 == 0:
return 2
elif id == 43 and num <= 18:
return 1
elif id == 44 and num > 18 and num <= 36:
return 1
elif id == 45 and num % 2 == 0:
return 1
elif id == 46 and num % 2 == 1:
return 1
elif id == 47 and num in RED_NUMBERS:
return 1
elif id == 48 and num not in RED_NUMBERS:
return 1
elif id < 37 and id == num:
return 35
else:
return -1
for i in range(len(bet_IDs)):
winnings = bet_Values[i] * get_modifier(bet_IDs[i],result)
total_winnings += winnings
if winnings >= 0:
print("YOU WIN " + str(winnings) + " DOLLARS ON BET " + str(i + 1))
else:
print("YOU LOSE " + str(winnings * -1) + " DOLLARS ON BET " + str(i + 1))
return winnings
def print_check(amount):
"""Prints a check of a given amount"""
name = input("TO WHOM SHALL I MAKE THE CHECK? ")
print("-" * 72)
print()
print(" " * 40 + "CHECK NO. " + str(random.randint(0,100)))
print(" " * 40 + str(date.today()))
print()
print("PAY TO THE ORDER OF -----" + name + "----- $" + str(amount))
print()
print(" " * 40 + "THE MEMORY BANK OF NEW YORK")
print(" " * 40 + "THE COMPUTER")
print(" " * 40 + "----------X-----")
print("-" * 72)
def main():
player_balance = 1000
host_balance = 100000
print(" " * 32 + "ROULETTE")
print(" " * 15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")
print()
print()
print()
if stringtobool(input("DO YOU WANT INSTRUCTIONS? ")):
print_instructions()
while True:
bet_IDs,bet_Values = query_bets()
print("SPINNING")
print()
print()
val = random.randint(0,38)
if val == 38:
print("0")
elif val == 37:
print("00")
elif val in RED_NUMBERS:
print(str(val) + " RED")
else:
print(str(val) + " BLACK")
print()
total_winnings = bet_results(bet_IDs,bet_Values,val)
player_balance += total_winnings
host_balance -= total_winnings
print()
print("TOTALS:\tME\t\tYOU")
print("\t\t" + str(host_balance) + "\t" + str(player_balance))
if player_balance <= 0:
print("OOPS! YOU JUST SPENT YOUR LAST DOLLAR!")
break
elif host_balance <= 0:
print("YOU BROKE THE HOUSE!")
player_balance = 101000
break
if not stringtobool(input("PLAY AGAIN? ")):
break
if player_balance <= 0:
print("THANKS FOR YOUR MONEY")
print("I'LL USE IT TO BUY A SOLID GOLD ROULETTE WHEEL")
else:
print_check(player_balance)
print("COME BACK SOON!")
def stringtobool(string):
"""Converts a string to a bool"""
return string.lower() in ("yes","y","true","t","yes")
if __name__ == '__main__':
main()
@@ -0,0 +1,39 @@
#!/usr/bin/perl
#use strict;
# Automatic converted by bas2perl.pl
print ' 'x28 . "RUSSIAN ROULETTE\n";
print ' 'x15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n";
print "\n"; print "\n"; print "\n";
print "THIS IS A GAME OF >>>>>>>>>>RUSSIAN ROULETTE.\n";
Line10:
print "\n"; print "HERE IS A REVOLVER.\n";
Line20:
print "TYPE '1' TO SPIN CHAMBER AND PULL TRIGGER.\n";
print "TYPE '2' TO GIVE UP.\n";
print "GO";
$N=0;
Line30:
print "? "; chomp($I = <STDIN>);
if ($I ne 2) { goto Line35; }
print " CHICKEN!!!!!\n";
goto Line72;
Line35:
$N=$N+1;
if (rand(1)>.833333) { goto Line70; }
if ($N>10) { goto Line80; }
print "- CLICK -\n";
print "\n"; goto Line30;
Line70:
print " BANG!!!!! YOU'RE DEAD!\n";
print "CONDOLENCES WILL BE SENT TO YOUR RELATIVES.\n";
Line72:
print "\n"; print "\n"; print "\n";
print "...NEXT VICTIM...\n"; goto Line20;
Line80:
print "YOU WIN!!!!!\n";
print "LET SOMEONE ELSE BLOW HIS BRAINS OUT.\n";
goto Line10;
exit;
+152
View File
@@ -0,0 +1,152 @@
$pot = 0
def greeting
puts "SLOTS".center(80)
puts "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY".center(80)
puts "\n\n"
# PRODUCED BY FRED MIRABELLE AND BOB HARPER ON JAN 29, 1973
# IT SIMULATES THE SLOT MACHINE.
puts "You are in the H&M Casino, in front of one of our"
puts "one-arm bandits. You can bet from $1 to $100."
puts "To pull the arm, punch the return key after making your bet."
puts "\nBet zero to end the game."
end
def overLimit
puts "House Limit is $100"
end
def underMinimum
puts "Minimum Bet is $1"
end
# bells don't work on my machine. YMMV
# I'm displaying dashes between the reels
def tenBells
10.times do
# beep if you can
print "-"
end
end
def fiveBells
"-----"
end
def goodbye
puts "\n\n\n"
# end the game
exit
end
def payUp
puts "PAY UP! PLEASE LEAVE YOUR MONEY ON THE TERMINAL."
end
def brokeEven
puts "HEY, YOU BROKE EVEN."
end
def collectWinnings
puts "COLLECT YOUR WINNINGS FROM THE H&M CASHIER."
end
def win winType, bet
case winType
when "jackpot"
winMessage = "***JACKPOT***"
winnings = 101
when "topDollar"
winMessage = "**TOP DOLLAR**"
winnings = 11
when "doubleBar"
winMessage = "*DOUBLE BAR!!*"
winnings = 6
when "double"
winMessage = "DOUBLE!!"
winnings = 3
end
puts "\nYou won: " + winMessage
$pot += (winnings * bet)
end
greeting
#$pot = 0
while true
reelArray = ["BAR","BELL","ORANGE","LEMON","PLUM","CHERRY"]
print "\nYOUR BET? "
# get input, remove leading and trailing whitespace, cast to integer
bet = gets.strip.to_i
if bet == 0 then
goodbye
elsif bet > 100 then
overLimit # error if more than $100
elsif bet < 1 then
underMinimum # have to bet at least a dollar
else
# valid bet, continue
tenBells # ding
# assign a random value from the array to each of the three reels
reel1 = reelArray[rand(5)]
reel2 = reelArray[rand(5)]
reel3 = reelArray[rand(5)]
# print the slot machine reels
puts "\n\n" + reel1 + fiveBells + reel2 + fiveBells + reel3
# see if we have a match in the first two reels
if reel1 == reel2 then
if reel2 == reel3 then
if reel3 == "BAR" then
# all three reels are "BAR"
win "jackpot", bet
else
# all three reels match but aren't "BAR"
win "topDollar", bet
end
elsif reel2 == "BAR" then
# reels 1 and 2 are both "BAR"
win "doubleBar", bet
else
# reels 1 and 2 match but aren't "BAR"
win "double", bet
end
# otherwise see if there's a match in the remaining reels
elsif reel1 == reel3 or reel2 == reel3 then
if reel3 == "BAR" then
# two reels match, both "BAR"
win "doubleBar", bet
else
# two reels match, but not "BAR"
win "double", bet
end
else
# bad news - no matches
puts "\nYou lost"
# decrement your standings by the bet amount
$pot -= bet
end
puts "Your standings are: " + $pot.to_s
print "\nAgain? " # YES to continue
# get input, remove leading and trailing whitespace, make uppercase
again = gets.strip.upcase
if again != "Y" && again != "YES" then
# that's enough... evaluate the pot and quit
if $pot < 0 then
payUp
elsif $pot == 0 then
brokeEven
else # yay!
collectWinnings
end
goodbye
end
end
end
+12
View File
@@ -25,6 +25,8 @@ Each project has subfolders corresponding to the languages we'd like to see the
- Delphi / Object Pascal
- Perl
### Project goals
Feel free to begin converting these classic games into the above list of modern, memory safe languages. But first, a few guidelines:
@@ -39,6 +41,16 @@ Feel free to begin converting these classic games into the above list of modern,
- **Don't get _too_ fancy**. Definitely use the most recent versions and features of the target language, but also try to keep the code samples simple and explainable -- the goal is to teach programming in the target language, not necessarily demonstrate the cleverest one-line tricks.
### Emulation and Bugfixes
We want the general behavior of the original programs to be preserved, _however_, we also want to update them, specifically:
- allow both UPPERCASE and lowercase input and display
- incorporate any bugfixes to the original programs; see the `readme.md` in the game folder
- improved error handling for bad or erroneous input
Please note that on the back of the Basic Computer Games book it says **Microsoft 8K Basic, Rev 4.0 was the version David Ahl used to test**, so that is the level of compatibility we are looking for.  QBasic on the DOS emulation is a later version of Basic but one that retains downwards compatibility so far in our testing. We're working on a recommended emulation to verify behavior.
### Have fun!
Thank you for taking part in this project to update a classic programming book -- one of the most influential programming books in computing history -- for 2022 and beyond!
+1 -1
View File
@@ -7,7 +7,7 @@ We should be using version 17 anyway, because anything less than 17 is deprecate
Build all the games:
```shell
cd buildJvm
./gradlew -q assemble installDist distributeBin distributeLib
./gradlew -q clean assemble installDist distributeBin distributeLib
```
Then, run a game