diff --git a/70_Poetry/csharp/Context.cs b/70_Poetry/csharp/Context.cs new file mode 100644 index 00000000..a9297915 --- /dev/null +++ b/70_Poetry/csharp/Context.cs @@ -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; + } +} diff --git a/70_Poetry/csharp/Phrase.cs b/70_Poetry/csharp/Phrase.cs new file mode 100644 index 00000000..f70de30b --- /dev/null +++ b/70_Poetry/csharp/Phrase.cs @@ -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 _condition; + private readonly string _text; + private readonly Action _update; + + private Phrase(Predicate condition, string text) + : this(condition, text, _ => { }) + { + } + + private Phrase(string text, Action update) + : this(_ => true, text, update) + { + } + + private Phrase(string text) + : this(_ => true, text, _ => { }) + { + } + + private Phrase(Predicate condition, string text, Action 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); + } +} \ No newline at end of file diff --git a/70_Poetry/csharp/Poem.cs b/70_Poetry/csharp/Poem.cs new file mode 100644 index 00000000..fa3d5045 --- /dev/null +++ b/70_Poetry/csharp/Poem.cs @@ -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; } + } + } + } +} \ No newline at end of file diff --git a/70_Poetry/csharp/Poetry.csproj b/70_Poetry/csharp/Poetry.csproj index d3fe4757..3870320c 100644 --- a/70_Poetry/csharp/Poetry.csproj +++ b/70_Poetry/csharp/Poetry.csproj @@ -6,4 +6,12 @@ enable enable + + + + + + + + diff --git a/70_Poetry/csharp/Program.cs b/70_Poetry/csharp/Program.cs new file mode 100644 index 00000000..3a17d72c --- /dev/null +++ b/70_Poetry/csharp/Program.cs @@ -0,0 +1,5 @@ +global using Games.Common.IO; +global using Games.Common.Randomness; +global using Poetry; + +Poem.Compose(new ConsoleIO(), new RandomNumberGenerator()); diff --git a/70_Poetry/csharp/Resources/Resource.cs b/70_Poetry/csharp/Resources/Resource.cs new file mode 100644 index 00000000..b789f035 --- /dev/null +++ b/70_Poetry/csharp/Resources/Resource.cs @@ -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}'."); +} \ No newline at end of file diff --git a/70_Poetry/csharp/Resources/Title.txt b/70_Poetry/csharp/Resources/Title.txt new file mode 100644 index 00000000..86161340 --- /dev/null +++ b/70_Poetry/csharp/Resources/Title.txt @@ -0,0 +1,5 @@ + Poetry + Creative Computing Morristown, New Jersey + + + diff --git a/70_Poetry/poetry.bas b/70_Poetry/poetry.bas index 3661287d..7fa3a1c4 100644 --- a/70_Poetry/poetry.bas +++ b/70_Poetry/poetry.bas @@ -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