This commit is contained in:
Troy Campbell
2022-10-14 12:24:14 +10:00
18 changed files with 910 additions and 16 deletions

View File

@@ -1,4 +1,4 @@
use std::ffi::{OsString, OsStr};
use std::ffi::OsStr;
use std::{fs, io};
use std::fs::metadata;
use std::path::{Path, PathBuf};
@@ -8,8 +8,6 @@ use std::path::{Path, PathBuf};
* @author Anthony Rubick
*/
//DATA
const ROOT_DIR: &str = "../../";
const LANGUAGES: [(&str,&str); 10] = [ //first element of tuple is the language name, second element is the file extension
@@ -25,13 +23,19 @@ const LANGUAGES: [(&str,&str); 10] = [ //first element of tuple is the language
("vbnet", "vb")
];
const OUTPUT_PATH: &str = "../../todo.md";
//const INGORE: [&str;5] = ["../../.git","../../.vscode","../../00_Utilities","../../buildJvm","../../node_modules"]; //folders to ignore
fn main() {
//DATA
let mut root_folders:Vec<PathBuf>;
let mut output_string: String = String::new();
let format_game_first: bool;
let ingore: [PathBuf;5] = [
PathBuf::from(r"../../.git"),
PathBuf::from(r"../../.github"),
PathBuf::from(r"../../00_Alternate_Languages"),
PathBuf::from(r"../../00_Utilities"),
PathBuf::from(r"../../00_Common"),
]; //folders to ignore
//print welcome message
println!("
@@ -66,15 +70,10 @@ fn main() {
//for all folders, search for the languages and extensions
root_folders = root_folders.into_iter().filter(|path| {
match fs::read_dir(path) {
Err(why) => {println!("! {:?}", why.kind()); false},
Ok(paths) => {
paths.into_iter().filter(|f| metadata(f.as_ref().unwrap().path()).unwrap().is_dir()) //filter to only folders
.filter_map( |path| path.ok() ) //extract only the DirEntries
.any(|f| LANGUAGES.iter().any(|tup| OsString::from(tup.1).eq_ignore_ascii_case(f.file_name()))) //filter out ones that don't contain folders with the language names
}
}
//not one of the ignored folders
!ingore.contains(path)
}).collect();
root_folders.sort();
//create todo list
if format_game_first {

View File

@@ -0,0 +1,8 @@
[package]
name = "rust"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@@ -0,0 +1,3 @@
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
Conversion to [Rust](https://www.rust-lang.org/) by [Uğur Küpeli](https://github.com/ugurkupeli)

View File

@@ -0,0 +1,152 @@
use std::io::stdin;
const WIDTH: usize = 64;
const DAYS_WIDTH: usize = WIDTH / 8;
const MONTH_WIDTH: usize = WIDTH - (DAYS_WIDTH * 2);
const DAY_NUMS_WIDTH: usize = WIDTH / 7;
const DAYS: [&str; 7] = [
"SUNDAY",
"MONDAY",
"TUESDAY",
"WEDNESDAY",
"THURSDAY",
"FRIDAY",
"SATURDAY",
];
fn main() {
println!("\n\t\t CALENDAR");
println!("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n");
let (starting_day, leap_year) = prompt();
let (months, total_days) = get_months_and_days(leap_year);
let mut days_passed = 0;
let mut current_day_index = DAYS.iter().position(|d| *d == starting_day).unwrap();
for (month, days) in months {
print_header(month, days_passed, total_days - days_passed);
print_days(&mut current_day_index, days);
days_passed += days as u16;
println!("\n");
}
}
fn prompt() -> (String, bool) {
let mut day = String::new();
loop {
println!("\nFirst day of the year?");
if let Ok(_) = stdin().read_line(&mut day) {
day = day.trim().to_uppercase();
if DAYS.contains(&day.as_str()) {
break;
} else {
day.clear();
}
}
}
let mut leap = false;
loop {
println!("Is this a leap year?");
let mut input = String::new();
if let Ok(_) = stdin().read_line(&mut input) {
match input.to_uppercase().trim() {
"Y" | "YES" => {
leap = true;
break;
}
"N" | "NO" => break,
_ => (),
}
}
}
println!();
(day, leap)
}
fn get_months_and_days(leap_year: bool) -> (Vec<(String, u8)>, u16) {
let months = [
"JANUARY",
"FEBUARY",
"MARCH",
"APRIL",
"MAY",
"JUNE",
"JULY",
"AUGUST",
"SEPTEMBER",
"OCTOBER",
"NOVEMBER",
"DECEMBER",
];
let mut months_with_days = Vec::new();
let mut total_days: u16 = 0;
for (i, month) in months.iter().enumerate() {
let days = if i == 1 {
if leap_year {
29u8
} else {
28
}
} else if if i < 7 { (i % 2) == 0 } else { (i % 2) != 0 } {
31
} else {
30
};
total_days += days as u16;
months_with_days.push((month.to_string(), days));
}
(months_with_days, total_days)
}
fn print_between(s: String, w: usize, star: bool) {
let s = format!(" {s} ");
if star {
print!("{:*^w$}", s);
return;
}
print!("{:^w$}", s);
}
fn print_header(month: String, days_passed: u16, days_left: u16) {
print_between(days_passed.to_string(), DAYS_WIDTH, true);
print_between(month.to_string(), MONTH_WIDTH, true);
print_between(days_left.to_string(), DAYS_WIDTH, true);
println!();
for d in DAYS {
let d = d.chars().nth(0).unwrap();
print_between(d.to_string(), DAY_NUMS_WIDTH, false);
}
println!();
println!("{:*>WIDTH$}", "");
}
fn print_days(current_day_index: &mut usize, days: u8) {
let mut current_date = 1u8;
print!("{:>w$}", " ", w = DAY_NUMS_WIDTH * *current_day_index);
for _ in 1..=days {
print_between(current_date.to_string(), DAY_NUMS_WIDTH, false);
if ((*current_day_index + 1) % 7) == 0 {
*current_day_index = 0;
println!();
} else {
*current_day_index += 1;
}
current_date += 1;
}
}

View File

@@ -17,5 +17,7 @@ http://www.vintage-basic.net/games.html
(please note any difficulties or challenges in porting here)
There is a type in the original Basic, "...DECIDE **WHO** MUCH WATER..." should be "DECIDE **HOW** MUCH WATER"
#### External Links
- C: https://github.com/ericfischer/basic-computer-games/blob/main/24%20Chemist/c/chemist.c

View File

@@ -1,3 +1,17 @@
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
Conversion to [Lua](https://www.lua.org/)
Conversion to [Lua](https://www.lua.org/) by Alex Conconi
---
### Lua porting notes
- The `craps_main` function contains the main game loop, which iteratively
plays craps rounds by calling `play_round` and tracks winnings and losings.
- Replaced the original routine that tries to scramble the random number
generator with a proper seed initializer in Lua: `math.randomseed(os.time())`
(as advised in the general porting notes).
- Added basic input validation to accept only positive integers for the
wager and the answer to the "If you want to play again print 5" question.
- "If you want to play again print 5 if not print 2" reads a bit odd but
we decided to leave it as is and stay true to the BASIC original version.

149
29_Craps/lua/craps.lua Normal file
View File

@@ -0,0 +1,149 @@
--[[
Craps
From: BASIC Computer Games (1978)
Edited by David H. Ahl
This game simulates the games of craps played according to standard
Nevada craps table rules. That is:
1. A 7 or 11 on the first roll wins
2. A 2, 3, or 12 on the first roll loses
3. Any other number rolled becomes your "point." You continue to roll;
if you get your point you win. If you roll a 7, you lose and the dice
change hands when this happens.
This version of craps was modified by Steve North of Creative Computing.
It is based on an original which appeared one day one a computer at DEC.
Lua port by Alex Conconi, 2022
--]]
--- Throw two dice and return their sum.
local function throw_dice()
return math.random(1, 6) + math.random(1, 6)
end
--- Print prompt and read a number > 0 from stdin.
local function input_number(prompt)
while true do
io.write(prompt)
local number = tonumber(io.stdin:read("*l"))
if number and number > 0 then
return number
else
print("Please enter a number greater than zero.")
end
end
end
--- Print custom balance message depending on balance value
local function print_balance(balance, under_msg, ahead_msg, even_msg)
if balance < 0 then
print(under_msg)
elseif balance > 0 then
print(ahead_msg)
else
print(even_msg)
end
end
--- Play a round and return winnings or losings.
local function play_round()
-- Input the wager
local wager = input_number("Input the amount of your wager: ")
-- Roll the die for the first time.
print("I will now throw the dice")
local first_roll = throw_dice()
-- A 7 or 11 on the first roll wins.
if first_roll == 7 or first_roll == 11 then
print(string.format("%d - natural.... a winner!!!!", first_roll))
print(string.format("%d pays even money, you win %d dollars", first_roll, wager))
return wager
end
-- A 2, 3, or 12 on the first roll loses.
if first_roll == 2 or first_roll == 3 or first_roll == 12 then
if first_roll == 2 then
-- Special 'you lose' message for 'snake eyes'
print(string.format("%d - snake eyes.... you lose.", first_roll))
else
-- Default 'you lose' message
print(string.format("%d - craps.... you lose.", first_roll))
end
print(string.format("You lose %d dollars", wager))
return -wager
end
-- Any other number rolled becomes the "point".
-- Continue to roll until rolling a 7 or point.
print(string.format("%d is the point. I will roll again", first_roll))
while true do
local second_roll = throw_dice()
if second_roll == first_roll then
-- Player gets point and wins
print(string.format("%d - a winner.........congrats!!!!!!!!", first_roll))
print(string.format("%d at 2 to 1 odds pays you...let me see... %d dollars", first_roll, 2 * wager))
return 2 * wager
end
if second_roll == 7 then
-- Player rolls a 7 and loses
print(string.format("%d - craps. You lose.", second_roll))
print(string.format("You lose $ %d", wager))
return -wager
end
-- Continue to roll
print(string.format("%d - no point. I will roll again", second_roll))
end
end
--- Main game function.
local function craps_main()
-- Print the introduction to the game
print(string.rep(" ", 32) .. "Craps")
print(string.rep(" ", 14) .. "Creative Computing Morristown, New Jersey\n\n")
print("2,3,12 are losers; 4,5,6,8,9,10 are points; 7,11 are natural winners.")
-- Initialize random number generator seed
math.randomseed(os.time())
-- Initialize balance to track winnings and losings
local balance = 0
-- Main game loop
local keep_playing = true
while keep_playing do
-- Play a round
balance = balance + play_round()
-- If player's answer is 5, then stop playing
keep_playing = (input_number("If you want to play again print 5 if not print 2: ") == 5)
-- Print an update on winnings or losings
print_balance(
balance,
string.format("You are now under $%d", -balance),
string.format("You are now ahead $%d", balance),
"You are now even at 0"
)
end
-- Game over, print the goodbye message
print_balance(
balance,
"Too bad, you are in the hole. Come again.",
"Congratulations---you came out a winner. Come again.",
"Congratulations---you came out even, not bad for an amateur"
)
end
--- Run the game.
craps_main()

8
55_Life/rust/Cargo.toml Normal file
View File

@@ -0,0 +1,8 @@
[package]
name = "rust"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

22
55_Life/rust/README.md Normal file
View File

@@ -0,0 +1,22 @@
# Conway's Life
Original from David Ahl's _Basic Computer Games_, downloaded from http://www.vintage-basic.net/games.html.
Ported to Rust by Jon Fetter-Degges
Developed and tested on Rust 1.64.0
## How to Run
Install Rust using the instructions at [rust-lang.org](https://www.rust-lang.org/tools/install).
At a command or shell prompt in the `rust` subdirectory, enter `cargo run`.
## Differences from Original Behavior
* The simulation stops if all cells die.
* `.` at the beginning of an input line is supported but optional.
* Input of more than 66 columns is rejected. Input will automatically terminate after 20 rows. Beyond these bounds, the original
implementation would have marked the board as invalid, and beyond 68 cols/24 rows it would have had an out of bounds array access.
* The check for the string "DONE" at the end of input is case-independent.
* The program pauses for half a second between each generation.

275
55_Life/rust/src/main.rs Normal file
View File

@@ -0,0 +1,275 @@
// Rust implementation of the "Basic Computer Games" version of Conway's Life
//
// Jon Fetter-Degges
// October 2022
// I am a Rust newbie. Corrections and suggestions are welcome.
use std::{cmp, fmt, io, thread, time};
// The BASIC implementation uses integers to represent the state of each cell: 1 is
// alive, 2 is about to die, 3 is about to be born, 0 is dead. Here, we'll use an enum
// instead.
// Deriving Copy (which requires Clone) allows us to use this enum value in assignments,
// and deriving Eq (or PartialEq) allows us to use the == operator. These need to be
// explicitly specified because some enums may have associated data that makes copies and
// comparisons more complicated or expensive.
#[derive(Clone, Copy, PartialEq, Eq)]
enum CellState {
Empty,
Alive,
AboutToDie,
AboutToBeBorn,
}
// Support direct printing of the cell. In this program cells will only be Alive or Empty
// when they are printed.
impl fmt::Display for CellState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let rep = match *self {
CellState::Empty => ' ',
CellState::Alive => '*',
CellState::AboutToDie => 'o',
CellState::AboutToBeBorn => '.',
};
write!(f, "{}", rep)
}
}
// Following the BASIC implementation, we will bound the board at 24 rows x 70 columns.
// The board is an array of CellState. Using an array of arrays gives us bounds checking
// in both dimensions.
const HEIGHT: usize = 24;
const WIDTH: usize = 70;
struct Board {
cells: [[CellState; WIDTH]; HEIGHT],
min_row: usize,
max_row: usize,
min_col: usize,
max_col: usize,
population: usize,
generation: usize,
invalid: bool,
}
impl Board {
fn new() -> Board {
Board {
cells: [[CellState::Empty; WIDTH]; HEIGHT],
min_row: 0,
max_row: 0,
min_col: 0,
max_col: 0,
population: 0,
generation: 0,
invalid: false,
}
}
}
fn main() {
println!(); println!(); println!();
println!("{:33}{}", " ", "Life");
println!("{:14}{}", " ", "Creative Computing Morristown, New Jersey");
println!("Enter your pattern: ");
let mut board = parse_pattern(get_pattern());
loop {
finish_cell_transitions(&mut board);
print_board(&board);
mark_cell_transitions(&mut board);
if board.population == 0 {
break; // this isn't in the original implementation but it seemed better than
// spewing blank screens
}
delay();
}
}
fn get_pattern() -> Vec<Vec<char>> {
let max_line_len = WIDTH - 4;
let max_line_count = HEIGHT - 4;
let mut lines = Vec::new();
loop {
let mut line = String::new();
// read_line reads into the buffer (appending if it's not empty). It returns the
// number of characters read, including the newline. This will be 0 on EOF.
// unwrap() will panic and terminate the program if there is an error reading
// from stdin. That's reasonable behavior in this case.
let nread = io::stdin().read_line(&mut line).unwrap();
let line = line.trim_end();
if nread == 0 || line.eq_ignore_ascii_case("DONE") {
return lines;
}
// Handle Unicode by converting the string to a vector of characters up front. We
// do this here because we check the number of characters several times, so we
// might as well just do the Unicode parsing once.
let line = Vec::from_iter(line.chars());
if line.len() > max_line_len {
println!("Line too long - the maximum is {max_line_len} characters.");
continue;
}
lines.push(line);
if lines.len() == max_line_count {
println!("Maximum line count reached. Starting simulation.");
return lines;
}
}
}
fn parse_pattern(rows: Vec<Vec<char>>) -> Board {
// This function assumes that the input pattern in rows is in-bounds. If the pattern
// is too large, this function will panic. get_pattern checks the size of the input,
// so it is safe to call this function with its results.
let mut board = Board::new();
// The BASIC implementation puts the pattern roughly in the center of the board,
// assuming that there are no blank rows at the beginning or end, or blanks entered
// at the beginning or end of every row. It wouldn't be hard to check for that, but
// for now we'll preserve the original behavior.
let nrows = rows.len();
// If rows is empty, the call to max will return None. The unwrap_or then provides a
// default value
let ncols = rows.iter().map(|l| l.len()).max().unwrap_or(0);
// The min and max values here are unsigned. If nrows >= 24 or ncols >= 68, these
// assignments will panic - they do not wrap around unless we use a function with
// that specific behavior. Again, we expect bounds checking on the input before this
// function is called.
board.min_row = 11 - nrows / 2;
board.min_col = 33 - ncols / 2;
board.max_row = board.min_row + nrows - 1;
board.max_col = board.min_col + ncols - 1;
// Loop over the rows provided. enumerate() augments the iterator with an index.
for (row_index, pattern) in rows.iter().enumerate() {
let row = board.min_row + row_index;
// Now loop over the non-empty cells in the current row. filter_map takes a
// closure that returns an Option. If the Option is None, filter_map filters out
// that entry from the for loop. If it's Some(x), filter_map executes the loop
// body with the value x.
for col in pattern.iter().enumerate().filter_map(|(col_index, chr)| {
if *chr == ' ' || (*chr == '.' && col_index == 0) {
None
} else {
Some(board.min_col + col_index)
}
}) {
board.cells[row][col] = CellState::Alive;
board.population += 1;
}
}
board
}
fn finish_cell_transitions(board: &mut Board) {
// In the BASIC implementation, this happens in the same loop that prints the board.
// We're breaking it out to improve separation of concerns.
let mut min_row = HEIGHT - 1;
let mut max_row = 0usize;
let mut min_col = WIDTH - 1;
let mut max_col = 0usize;
for row_index in board.min_row-1..=board.max_row+1 {
let mut any_alive_this_row = false;
for col_index in board.min_col-1..=board.max_col+1 {
let cell = &mut board.cells[row_index][col_index];
if *cell == CellState::AboutToBeBorn {
*cell = CellState::Alive;
board.population += 1;
} else if *cell == CellState::AboutToDie {
*cell = CellState::Empty;
board.population -= 1;
}
if *cell == CellState::Alive {
any_alive_this_row = true;
min_col = cmp::min(min_col, col_index);
max_col = cmp::max(max_col, col_index);
}
}
if any_alive_this_row {
min_row = cmp::min(min_row, row_index);
max_row = cmp::max(max_row, row_index);
}
}
// If anything is alive within two cells of the boundary, mark the board invalid and
// clamp the bounds. We need a two-cell margin because we'll count neighbors on cells
// one space outside the min/max, and when we count neighbors we go out by an
// additional space.
if min_row < 2 {
min_row = 2;
board.invalid = true;
}
if max_row > HEIGHT - 3 {
max_row = HEIGHT - 3;
board.invalid = true;
}
if min_col < 2 {
min_col = 2;
board.invalid = true;
}
if max_col > WIDTH - 3 {
max_col = WIDTH - 3;
board.invalid = true;
}
board.min_row = min_row;
board.max_row = max_row;
board.min_col = min_col;
board.max_col = max_col;
}
fn print_board(board: &Board) {
println!(); println!(); println!();
print!("Generation: {} Population: {}", board.generation, board.population);
if board.invalid {
print!(" Invalid!");
}
println!();
for row_index in 0..HEIGHT {
for col_index in 0..WIDTH {
// This print uses the Display implementation for cell_state, above.
print!("{}", board.cells[row_index][col_index]);
}
println!();
}
}
fn count_neighbors(board: &Board, row_index: usize, col_index: usize) -> i32 {
// Simply loop over all the immediate neighbors of a cell. We assume that the row and
// column indices are not on (or outside) the boundary of the arrays; if they are,
// the function will panic instead of going out of bounds.
let mut count = 0;
for i in row_index-1..=row_index+1 {
for j in col_index-1..=col_index+1 {
if i == row_index && j == col_index {
continue;
}
if board.cells[i][j] == CellState::Alive || board.cells[i][j] == CellState::AboutToDie {
count += 1;
}
}
}
count
}
fn mark_cell_transitions(board: &mut Board) {
for row_index in board.min_row-1..=board.max_row+1 {
for col_index in board.min_col-1..=board.max_col+1 {
let neighbors = count_neighbors(board, row_index, col_index);
// Borrow a mutable reference to the array cell
let this_cell_state = &mut board.cells[row_index][col_index];
*this_cell_state = match *this_cell_state {
CellState::Empty if neighbors == 3 => CellState::AboutToBeBorn,
CellState::Alive if !(2..=3).contains(&neighbors) => CellState::AboutToDie,
_ => *this_cell_state,
}
}
}
board.generation += 1;
}
fn delay() {
thread::sleep(time::Duration::from_millis(500));
}

109
70_Poetry/csharp/Context.cs Normal file
View File

@@ -0,0 +1,109 @@
namespace Poetry;
internal class Context
{
private readonly IReadWrite _io;
private readonly IRandom _random;
private int _phraseNumber;
private int _groupNumber;
private bool _skipComma;
private int _lineCount;
private bool _useGroup2;
private bool _atStartOfLine = true;
public Context(IReadWrite io, IRandom random)
{
_io = io;
_random = random;
}
public int PhraseNumber => Math.Max(_phraseNumber - 1, 0);
public int GroupNumber
{
get
{
var value = _useGroup2 ? 2 : _groupNumber;
_useGroup2 = false;
return Math.Max(value - 1, 0);
}
}
public int PhraseCount { get; set; }
public bool GroupNumberIsValid => _groupNumber < 5;
public void WritePhrase()
{
Phrase.GetPhrase(this).Write(_io, this);
_atStartOfLine = false;
}
public void MaybeWriteComma()
{
if (!_skipComma && _random.NextFloat() <= 0.19F && PhraseCount != 0)
{
_io.Write(",");
PhraseCount = 2;
}
_skipComma = false;
}
public void WriteSpaceOrNewLine()
{
if (_random.NextFloat() <= 0.65F)
{
_io.Write(" ");
PhraseCount += 1;
}
else
{
EndLine();
PhraseCount = 0;
}
}
public void Update(IRandom random)
{
_phraseNumber = random.Next(1, 6);
_groupNumber += 1;
_lineCount += 1;
}
public void MaybeIndent()
{
if (PhraseCount == 0 && _groupNumber % 2 == 0)
{
_io.Write(" ");
}
}
public void ResetGroup()
{
_groupNumber = 0;
EndLine();
}
public bool MaybeCompleteStanza()
{
if (_lineCount > 20)
{
_io.WriteLine();
PhraseCount = _lineCount = 0;
_useGroup2 = true;
return true;
}
return false;
}
internal string MaybeCapitalise(string text) =>
_atStartOfLine ? (char.ToUpper(text[0]) + text[1..]) : text;
public void SkipNextComma() => _skipComma = true;
public void EndLine()
{
_io.WriteLine();
_atStartOfLine = true;
}
}

View File

@@ -0,0 +1,78 @@
namespace Poetry;
internal class Phrase
{
private readonly static Phrase[][] _phrases = new Phrase[][]
{
new Phrase[]
{
new("midnight dreary"),
new("fiery eyes"),
new("bird or fiend"),
new("thing of evil"),
new("prophet")
},
new Phrase[]
{
new("beguiling me", ctx => ctx.PhraseCount = 2),
new("thrilled me"),
new("still sitting....", ctx => ctx.SkipNextComma()),
new("never flitting", ctx => ctx.PhraseCount = 2),
new("burned")
},
new Phrase[]
{
new("and my soul"),
new("darkness there"),
new("shall be lifted"),
new("quoth the raven"),
new(ctx => ctx.PhraseCount != 0, "sign of parting")
},
new Phrase[]
{
new("nothing more"),
new("yet again"),
new("slowly creeping"),
new("...evermore"),
new("nevermore")
}
};
private readonly Predicate<Context> _condition;
private readonly string _text;
private readonly Action<Context> _update;
private Phrase(Predicate<Context> condition, string text)
: this(condition, text, _ => { })
{
}
private Phrase(string text, Action<Context> update)
: this(_ => true, text, update)
{
}
private Phrase(string text)
: this(_ => true, text, _ => { })
{
}
private Phrase(Predicate<Context> condition, string text, Action<Context> update)
{
_condition = condition;
_text = text;
_update = update;
}
public static Phrase GetPhrase(Context context) => _phrases[context.GroupNumber][context.PhraseNumber];
public void Write(IReadWrite io, Context context)
{
if (_condition.Invoke(context))
{
io.Write(context.MaybeCapitalise(_text));
}
_update.Invoke(context);
}
}

32
70_Poetry/csharp/Poem.cs Normal file
View File

@@ -0,0 +1,32 @@
using static Poetry.Resources.Resource;
namespace Poetry;
internal class Poem
{
internal static void Compose(IReadWrite io, IRandom random)
{
io.Write(Streams.Title);
var context = new Context(io, random);
while (true)
{
context.WritePhrase();
context.MaybeWriteComma();
context.WriteSpaceOrNewLine();
while (true)
{
context.Update(random);
context.MaybeIndent();
if (context.GroupNumberIsValid) { break; }
context.ResetGroup();
if (context.MaybeCompleteStanza()) { break; }
}
}
}
}

View File

@@ -6,4 +6,12 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Resources/*.txt" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\00_Common\dotnet\Games.Common\Games.Common.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,5 @@
global using Games.Common.IO;
global using Games.Common.Randomness;
global using Poetry;
Poem.Compose(new ConsoleIO(), new RandomNumberGenerator());

View File

@@ -0,0 +1,16 @@
using System.Reflection;
using System.Runtime.CompilerServices;
namespace Poetry.Resources;
internal static class Resource
{
internal static class Streams
{
public static Stream Title => GetStream();
}
private static Stream GetStream([CallerMemberName] string? name = null) =>
Assembly.GetExecutingAssembly().GetManifestResourceStream($"{typeof(Resource).Namespace}.{name}.txt")
?? throw new Exception($"Could not find embedded resource stream '{name}'.");
}

View File

@@ -0,0 +1,5 @@
Poetry
Creative Computing Morristown, New Jersey

View File

@@ -1,3 +1,8 @@
5 Y=RND(-1)
6 REM FOR X = 1 TO 100
7 REM PRINT RND(1);","
8 REM NEXT X
9 REM GOTO 999
10 PRINT TAB(30);"POETRY"
20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"
30 PRINT:PRINT:PRINT
@@ -26,17 +31,21 @@
133 PRINT "SLOWLY CREEPING";:GOTO 210
134 PRINT "...EVERMORE";:GOTO 210
135 PRINT "NEVERMORE";
210 IF U=0 OR RND(1)>.19 THEN 212
210 GOSUB 500 : IF U=0 OR X>.19 THEN 212
211 PRINT ",";:U=2
212 IF RND(1)>.65 THEN 214
212 GOSUB 500 : IF X>.65 THEN 214
213 PRINT " ";:U=U+1:GOTO 215
214 PRINT : U=0
215 I=INT(INT(10*RND(1))/2)+1
215 GOSUB 500 : I=INT(INT(10*X)/2)+1
220 J=J+1 : K=K+1
225 REM PRINT "I=";I;"; J=";J;"; K=";K;"; U=";U
230 IF U>0 OR INT(J/2)<>J/2 THEN 240
235 PRINT " ";
240 ON J GOTO 90,110,120,130,250
250 J=0 : PRINT : IF K>20 THEN 270
260 GOTO 215
270 PRINT : U=0 : K=0 : GOTO 110
500 X = RND(1)
505 REM PRINT "#";X;"#"
510 RETURN
999 END