// game logic private void StartNextRound() { if (IsDone) { try { LibraryLock.EnterWriteLock(); // reset the stats Stats.Reset(); lock (Ephemerials) { Ephemerials.Clear(); } // prepare a new poem CurrentLine = 0; CurrentBook = Library.Randomize(); CurrentPoem = Library.Randomize(CurrentBook); var stanza = Library.Randomize(CurrentPoem, maxChars: 100, maxCharsPerLine: 20); Lines = new Line[stanza.Count]; // construct the lines var lengths = new int[stanza.Count]; for (int i = 0; i < Lines.Length; i++) { Lines[i] = new Line(i, stanza[i]); lengths[i] = Lines[i].Text.Length; } // reset Cat.Reset(lengths, xStart: TextXStart, yStart: TextYStart + (TextSpacing * 5) - (Cat.Height * 0.75f), xDelta: FontWidth, yDelta: TextSpacing); IsDone = false; } finally { LibraryLock.ExitWriteLock(); } } }
public List <string> Randomize(Poem poem, int maxChars, int maxCharsPerLine) { // pick a random set of lines within this poem if (poem == null || poem.Lines.Count == 0 || maxChars <= 0 || maxCharsPerLine <= 0) { throw new Exception("Must provide a valid poem"); } var lines = new List <string>(); var start = 0; // get all the lengths so that a random start can be calculated // get the index of which we can still satisfy the 'maxChars' requirement // determine a random starting index var lengthSum = 0; for (int i = poem.Lines.Count - 1; i >= 0; i--) { lengthSum += poem.Lines[i].Length; if (lengthSum >= maxChars && i > 0) { start = Random.Next() % i; break; } } // grab 'maxChars' from the poem with a local maximum of 'maxCharsPerLine' lengthSum = maxChars; for (int i = start; lengthSum > 0 && i < poem.Lines.Count; i++) { var text = poem.Lines[i]; if (lengthSum - poem.Lines[i].Length < 0) { // this is the last line and it is too long var length = lengthSum; // find the end of the current word while (length < text.Length && text[length] != ' ') { length++; } text = poem.Lines[i].Substring(0, length); } // add lines that do not exceed 'maxCharsPerLine' var index = 0; while (index < text.Length) { var length = (text.Length - index) > maxCharsPerLine ? maxCharsPerLine : (text.Length - index); // ensure to break on a space while ((index + length) > 0 && (index + length) != text.Length && text[index + length - 1] != ' ') { length--; } if (length <= 0) { throw new Exception("Failed to find a space"); } lines.Add(text.Substring(index, length).Trim()); index += length; } lengthSum -= poem.Lines[i].Length; } return(lines); }
public static Book ParseMd(string[] lines) { // spec: // <head> // #Title: Title // #Author: Author // </head> // <book> // ' comment // #Section Header // #Name // </book> // transforms: // remove: // {...}, [...] (may be multiline, and more than 1 per line) // completely whitespace // leading and trailing whitespace // numbers (eg. line numbers) // replace: // ’, “, ”, —, è, _ var book = new Book(); var poems = new Dictionary <string /*section*/, Dictionary <string /*title*/, Poem> >(); var delineators = new Tuple <char, char>[] { new Tuple <char, char>('{', '}'), new Tuple <char, char>('[', ']') }; var state = ParseState.None; var section = ""; var title = ""; var validCharacters = new Regex("^[\\\" -abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,:;]+$"); for (int i = 0; i < lines.Length; i++) { if (string.IsNullOrWhiteSpace(lines[i])) { continue; } // trim leading and trailing whitespace lines[i] = lines[i].Trim(); // skip comments if (lines[i].StartsWith("'")) { continue; } // remove leading numbers var index = -1; while ((index + 1) < lines[i].Length && char.IsDigit(lines[i][index + 1])) { index++; } if (index >= 0) { lines[i] = lines[i].Substring(index + 1).Trim(); } // check state if (lines[i].Equals("<head>", StringComparison.OrdinalIgnoreCase)) { state = ParseState.Head; continue; } else if (lines[i].Equals("</head>", StringComparison.OrdinalIgnoreCase)) { state = ParseState.None; } else if (lines[i].Equals("<book>", StringComparison.OrdinalIgnoreCase)) { state = ParseState.Book; continue; } else if (lines[i].Equals("</book>", StringComparison.OrdinalIgnoreCase)) { state = ParseState.None; } if (state == ParseState.None) { continue; } // transform lines[i] = lines[i].Replace("’", "'") .Replace("‘", "'") .Replace("“", "\"") .Replace("”", "\"") .Replace(" ", " ") .Replace("—", "-") .Replace("_", " ") .Replace("è", "e") .Replace("á", "a") .Replace("ü", "u") .Replace("ē", "e") .Replace("é", "e") .Replace("æ", "a"); // remove {...} [...] foreach (var m in delineators) { // find the start and end of these delineators var lineStart = -1; var lineEnd = -1; var start = lines[i].IndexOf(m.Item1); var end = -1; var escape = 10; while (start >= 0) { lineStart = lineEnd = i; // find the end do { end = lines[lineEnd].IndexOf(m.Item2); if (end < 0) { lineEnd++; } }while (end < 0 && lineEnd < lines.Length && --escape >= 0); // hit the limit if (escape == 0) { throw new Exception("Failed to find closing delineator"); } // single line remove if (lineEnd == i) { var line = ""; if (start > 0) { line = lines[i].Substring(0, start - 1); } if ((end + 1) < lines[i].Length) { line += lines[i].Substring(end + 1); } lines[i] = line.Trim(); } // multi-line else { // first line if (start > 0) { lines[lineStart] = lines[lineStart].Substring(0, start - 1).TrimEnd(); } else { lines[lineStart] = ""; } // middlelines lineStart++; while (lineStart < lineEnd) { lines[lineStart++] = ""; } // last line if ((end + 1) < lines[lineEnd].Length) { lines[lineEnd] = lines[lineEnd].Substring(end + 1).TrimStart(); } else { lines[lineEnd] = ""; } } // check again start = lines[i].IndexOf(m.Item1); } } // remove empty lines if (string.IsNullOrWhiteSpace(lines[i])) { continue; } // parse if (state == ParseState.Head) { if (lines[i].ToLower().StartsWith("#title:")) { book.Title = lines[i].Substring(7).Trim(); } else if (lines[i].ToLower().StartsWith("#author:")) { book.Author = lines[i].Substring(8).Trim(); } } else if (state == ParseState.Book) { if (lines[i].StartsWith("##")) { title = lines[i].Substring(2).Trim(); } else if (lines[i].StartsWith("#")) { section = lines[i].Substring(1).Trim(); } else { if (string.IsNullOrWhiteSpace(section) || string.IsNullOrWhiteSpace(title)) { throw new Exception("Empty names"); } // add poem Dictionary <string, Poem> bySection = null; if (!poems.TryGetValue(section, out bySection)) { bySection = new Dictionary <string, Poem>(); poems.Add(section, bySection); } Poem poem = null; if (!bySection.TryGetValue(title, out poem)) { poem = new Poem() { Section = section, Title = title }; bySection.Add(title, poem); book.Poems.Add(poem); } // sanity check the line content if (!validCharacters.IsMatch(lines[i])) { throw new Exception("Failed on : " + lines[i]); } // add lines to the poem poem.Lines.Add(lines[i].Trim()); } } else { throw new Exception("Unknown state"); } } if (book.Poems.Count == 0) { throw new Exception("Failed to parse poems"); } return(book); }