private static bool Check(Board board, Rack rack, PlayPath path, bool verbose = true) { try { board.Play(path); var letters = new List <char>(rack.Letters); foreach (LetterPlay lp in path.Played) { if (!letters.Contains(lp.Letter)) { throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "{0} not in rack ({1})", lp.Letter, rack.Value)); } letters.Remove(lp.Letter); } return(true); } catch (OutOfBoardException e) { if (verbose) { Log(e); } return(false); } catch (ArgumentException e) { if (verbose) { Log(e); } return(false); } }
private static IEnumerable <Tuple <Fix, Cell> > GetFixCells(PlayPath path) { Direction direction = path.Main.Direction; var start = new List <Tuple <Fix, Cell> >(); switch (direction) { case Direction.Right: if (path.Main.First.HasLeft) { start.Add(new Tuple <Fix, Cell>(Fix.Prefix, path.Main.First.Left)); } if (path.Main.Last.HasRight) { start.Add(new Tuple <Fix, Cell>(Fix.Suffix, path.Main.Last.Right)); } break; case Direction.Down: if (path.Main.First.HasTop) { start.Add(new Tuple <Fix, Cell>(Fix.Prefix, path.Main.First.Up)); } if (path.Main.Last.HasBottom) { start.Add(new Tuple <Fix, Cell>(Fix.Suffix, path.Main.Last.Down)); } break; } return(start); }
[Test] public void TestPlaySecondNovice() { var part1 = new WordPart("LETTRE", new Cell(4, 3), Direction.Right); var board1 = new Board().Play(part1); var part2 = new WordPart("LE", new Cell(4, 3), Direction.Down); var play = new PlayPath(part2, new LetterPlay(new Cell(5, 3), 'E'), new ConstantList <char>("MOTUR".ToList())); var board2 = board1.Play(play); Assert.AreEqual(new PlayerScore(5, 1), board2.Score.Current); Assert.AreEqual(new PlayerScore(2, 0), board2.Score.Other); }
[Test] public void TestGetPathArgumentException() { var board = new Board(); var part1 = new WordPart("YEWS", new Cell(4, 4), Direction.Right); board = board.Play(part1); var rack = new Rack("ESZZZZ"); PlayPath path2 = Shell.GetPath(board, rack, "YES", 4, 4, null); Assert.IsNotNull(path2); }
[Test] public void TestPlaySecondSameDirection() { var part1 = new WordPart("ET", new Cell(4, 3), Direction.Right); var board1 = new Board().Play(part1); var part2 = new WordPart("LETTRE", new Cell(4, 2), Direction.Right); var played = LetterPlayTest.GetPlayed(part2, 1, 2); var play = new PlayPath(part2, new WordPartCollection(), played, new ConstantList <char>()); var board2 = board1.Play(play); Assert.AreEqual(new PlayerScore(0, 0), board2.Score.Current); Assert.AreEqual(new PlayerScore(6, 0), board2.Score.Other); }
[Test] public void TestPlayVortexAddStars() { var part1 = new WordPart("LETTRE", new Cell(4, 2), Direction.Right); var board1 = new Board().Play(part1); var part2 = new WordPart("JOUES", new Cell(0, 8), Direction.Down); var played = LetterPlayTest.GetPlayed(part2); var extra = part1.Play(new Cell(4, 8), 'S'); var play = new PlayPath(part2, new WordPartCollection(extra), played.ToConstant(), new ConstantList <char>()); var board2 = board1.Play(play); Assert.AreEqual(new PlayerScore(0, 0), board2.Score.Current); Assert.AreEqual(new PlayerScore(12, 0), board2.Score.Other); }
[Test] public void TestPlaySecondExpert() { var part1 = new WordPart("LETTRE", new Cell(4, 2), Direction.Right); var board1 = new Board().Play(part1); var part2 = new WordPart("MOTEUR", new Cell(1, 8), Direction.Down); var played = LetterPlayTest.GetPlayed(part2); var extra = part1.Play(new Cell(4, 8), 'E'); var play = new PlayPath(part2, new WordPartCollection(extra), played, new ConstantList <char>()); var board2 = board1.Play(play); Assert.AreEqual(new PlayerScore(0, 0), board2.Score.Current); Assert.AreEqual(new PlayerScore(12, 1), board2.Score.Other); }
public Choices(WordGraph graph, Board board, PlayPath path, Fix fix, Cell cell) { this.fix = fix; this.cell = cell; Direction direction = path.Main.Direction; var otherDirection = direction == Direction.Down ? Direction.Right : Direction.Down; var playable = new PlayableLetters(path.Pending); WordPart before = fix == Fix.Suffix ? path.Main : null; WordPart after = fix == Fix.Prefix ? path.Main : null; main = GetBeforeAfter(board, cell, direction, graph, playable, before, after); extra = GetBeforeAfter(board, cell, otherDirection, graph, playable); letters = playable.Choices.ToConstant(); }
internal static PlayPath GetPath(Board board, Rack rack, string word, int row, int column, Direction?direction = null) { Cell cell = GetCell(row, column); if (cell == null) { return(null); } PlayPath path = GetPath(board, rack, word, cell, direction); if (path == null) { return(null); } return(path); }
private static PlayPath GetPath(Board board, Rack rack, string word, Cell cell, Direction?direction) { var directions = new List <Direction>(); if (!direction.HasValue) { directions.AddRange(new[] { Direction.Down, Direction.Right }); } else { directions.Add(direction.Value); } var paths = new List <PlayPath>(); foreach (Direction d in directions) { try { var part = new WordPart(word, cell, d); PlayPath path = GetPath(board, rack, part); if (Check(board, rack, path, false)) { paths.Add(path); } } catch (ArgumentException) { } catch (OutOfBoardException) { } } if (paths.Count == 1) { return(paths[0]); } if (paths.Count > 1) { Log(new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Cannot guess direction to play {0} at {1}", word, cell))); } else { Log(new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Cannot play {0} at {1}", word, cell))); } return(null); }
private static void WriteMoves(IComparer <PlayInfo> comparer, IEnumerable <Tuple <double, PlayInfo, PlayPath> > moves) { Console.WriteLine(comparer); foreach (Tuple <double, PlayInfo, PlayPath> tuple in moves) { double weight = tuple.Item1; PlayInfo info = tuple.Item2; PlayPath path = tuple.Item3; Console.WriteLine("{0} word: {1} - {2}{3}{4}[{5}] ({6}){7}", weight.ToString("0.###", CultureInfo.InvariantCulture), path.Main, info.HasFixes && !info.HasVortex ? (info.HasOneFixes ? "1" : "") + (info.HasTwoMoreFixes ? "2+" : "") + "fixes " : "", info.HasVortex ? "vortex " : "", info.Points, info.Diff.ToString("+#;-#;0", CultureInfo.InvariantCulture), info.Stars, info.Wins ? " wins" : ""); } Console.WriteLine(); }
public Tuple <Fix, Fix> GetFixes(PlayPath path) { var min = new Dictionary <Direction, HashSet <int> >(); min.Add(Direction.Down, new HashSet <int>()); min.Add(Direction.Right, new HashSet <int>()); var max = new Dictionary <Direction, HashSet <int> >(); max.Add(Direction.Down, new HashSet <int>()); max.Add(Direction.Right, new HashSet <int>()); //var chrono = new Chrono(); var done = new HashSet <WordPart>(); var allValids = new Dictionary <WordPart, PlayPath>(); var temp = new PlayPath(path.Main, path.Extras, path.Played, null); GetNewPaths(graph, board, temp, temp, done, allValids, false, min, max); //double seconds = Convert.ToInt32(chrono.Elapsed.TotalSeconds * 10) * .1; //if (seconds > 0) // Console.WriteLine("Analyzed {0} fix paths in {1} s", done.Count, seconds); return(GetFixes(path.Main, allValids.Values)); }
private static Board TestPlayIgnoreExtra(IList <Tuple <string, string, Cell, Direction> > plays) { var graph = WordGraph.French; var board = new Board(); var rack = new Rack(plays[0].Item1); var part1 = new WordPart(plays[0].Item2, plays[0].Item3, plays[0].Item4); board = board.Play(part1); for (int i = 1; i < plays.Count; i++) { rack = new Rack(plays[i].Item1); PlayGraph play = new PlayGraph(graph, board, rack); var moveFinder = new MoveFinder(graph, board, play); List <Tuple <PlayInfo, PlayPath> > moves = moveFinder.GetAllMoves(); foreach (Tuple <PlayInfo, PlayPath> move in moves) { PlayPath path = move.Item2; if (plays[i].Item2 != path.Main.Word || plays[i].Item3 != path.Main.First || plays[i].Item4 != path.Main.Direction) { continue; } board = board.Play(path); if (i != plays.Count - 1) { continue; } Assert.GreaterOrEqual(board.Score.Other.Points, 0); Assert.GreaterOrEqual(board.Score.Current.Points, 0); return(board); } } throw new InvalidOperationException("Move not found"); }
public Board Play(PlayPath path) { return(Play(path.Main, path.Played, path.Extras)); }
private PlayPath ReadMove(Board board, Rack rack) { var regex = new Regex(@"^\s*(?<word>[a-z]+)\s*(?<row>\d),(?<column>\d)\s*(?<direction>(down|right|))\s*$", RegexOptions.IgnoreCase); using (DisposableColor.Prompt) Console.Write("move? "); Console.Write("[word r,c down|right] [play|guess|skip] "); while (true) { try { string line = null; using (new DisposableColor(PlayerScore.GetColor(board.Current))) line = ReadLine().Trim(); if (line == "skip") { return(null); } if (line == "play" || line == "guess") { if (board.IsEmpty) { var game = new Game(random, graph); WordPart part = game.GetFirstWord(rack); Console.WriteLine(part); Console.WriteLine(); var played = part.GetPlayed(); var pending = new List <char>(rack.Letters); foreach (LetterPlay lp in played) { pending.Remove(lp.Letter); } var path = new PlayPath(part, new WordPartCollection(), part.GetPlayed(), pending.ToConstant()); if (line == "play") { return(path); } } else { PlayPath path = FindMove(rack); if (line == "play") { return(path); } } } Match m = regex.Match(line); if (m.Success) { string word = m.Groups["word"].Value; int row = int.Parse(m.Groups["row"].Value, CultureInfo.InvariantCulture); int column = int.Parse(m.Groups["column"].Value, CultureInfo.InvariantCulture); Direction?direction = Parse(m.Groups["direction"].Value); PlayPath path = GetPath(board, rack, word, row, column, direction); if (path != null) { Console.WriteLine(); return(path); } } Console.Write("? "); } catch (FormatException e) { var color = Console.ForegroundColor; Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("{0} : {1}", e.GetType().Name, e.Message); Console.ForegroundColor = color; Console.Write("? "); } } }
internal static void GetNewPaths(WordGraph graph, Board board, PlayPath origin, PlayPath path, ISet <WordPart> done, IDictionary <WordPart, PlayPath> newValids, bool validOnly, Dictionary <Direction, HashSet <int> > min, Dictionary <Direction, HashSet <int> > max) { if (!validOnly) { //if (min[path.Main.Direction].Contains(0) && max[path.Main.Direction].Contains(8)) // return; //Console.WriteLine("{0}{1}", new string('.', path.Main.Direction == Direction.Bottom ? path.Main.First.Row : path.Main.First.Column), path.Main.Word); } IEnumerable <Tuple <Fix, Cell> > start = GetFixCells(path); foreach (Tuple <Fix, Cell> fixCell in start) { Fix fix = fixCell.Item1; Cell cell = fixCell.Item2; if (!validOnly) { if (fix == Fix.Prefix) { if (min[path.Main.Direction].Contains(0)) { break; } if (path.Main.Direction == Direction.Right) { if (origin.Main.First.Column - path.Main.First.Column >= 2 || path.Main.First.Column == 0) { break; } } else { if (origin.Main.First.Row - path.Main.First.Row >= 2 || path.Main.First.Row == 0) { break; } } } else { if (max[path.Main.Direction].Contains(0)) { break; } if (path.Main.Direction == Direction.Right) { if (path.Main.Last.Column - origin.Main.Last.Column >= 2 || path.Main.Last.Column == 8) { break; } } else { if (path.Main.Last.Row - origin.Main.Last.Row >= 2 || path.Main.Last.Row == 8) { break; } } } } var choices = new Choices(graph, board, path, fix, cell); if (choices.Letters.Count == 0) { continue; } GetNewPaths(graph, board, origin, path, done, newValids, choices, validOnly, min, max); } }
internal static void GetNewPaths(WordGraph graph, Board board, PlayPath origin, PlayPath path, ISet <WordPart> done, IDictionary <WordPart, PlayPath> newValids, Choices choices, bool validOnly, Dictionary <Direction, HashSet <int> > min, Dictionary <Direction, HashSet <int> > max) { foreach (char letter in choices.Letters) { WordPart mainPart = choices.Main.Play(choices.Cell, letter); if (done.Contains(mainPart)) { continue; } if (!validOnly) { if (choices.Fix == Fix.Prefix) { if (min[path.Main.Direction].Contains(0)) { break; } } else if (choices.Fix == Fix.Suffix) { if (max[path.Main.Direction].Contains(8)) { break; } } } WordPart extraPart = choices.Extra.Play(choices.Cell, letter); if (extraPart != null && (validOnly && !graph.IsValid(extraPart.Word) || !validOnly && !graph.Contains(extraPart.Word))) { continue; } ConstantList <char> pending = null; if (path.Pending != null) { var temp = new List <char>(path.Pending); temp.Remove(letter); if (temp.Count == 0 && !graph.IsValid(mainPart.Word)) { continue; } pending = temp.ToConstant(); } var extras = new List <WordPart>(path.Extras); if (extraPart != null) { extras.Add(extraPart); } var letterPlay = new LetterPlay(choices.Cell, letter); var played = new List <LetterPlay>(path.Played); if (choices.Fix == Fix.Prefix) { played.Insert(0, letterPlay); } else { played.Add(letterPlay); } bool valid = mainPart.Word.Length == 1 || graph.IsValid(mainPart.Word); var newPath = new PlayPath(mainPart, new WordPartCollection(extras.ToConstant()), played.ToConstant(), pending); if (valid && !newValids.ContainsKey(mainPart)) { newValids.Add(mainPart, newPath); } done.Add(mainPart); { if (!validOnly && valid) { min[mainPart.Direction].Add(mainPart.Direction == Direction.Right ? newPath.Main.First.Column : newPath.Main.First.Row); max[mainPart.Direction].Add(mainPart.Direction == Direction.Right ? newPath.Main.Last.Column : newPath.Main.Last.Row); if (choices.Fix == Fix.Prefix) { if (path.Main.Direction == Direction.Right) { if (origin.Main.First.Column - path.Main.First.Column >= 2 || path.Main.First.Column == 0) { break; } } else { if (origin.Main.First.Row - path.Main.First.Row >= 2 || path.Main.First.Row == 0) { break; } } } else { if (path.Main.Direction == Direction.Right) { if (path.Main.Last.Column - origin.Main.Last.Column >= 2 || path.Main.Last.Column == 8) { break; } } else { if (path.Main.Last.Row - origin.Main.Last.Row >= 2 || path.Main.Last.Row == 8) { break; } } } } GetNewPaths(graph, board, origin, newPath, done, newValids, validOnly, min, max); } } }
public void Run() { Rack rack = null; while (true) { try { bool skipped = false; var pending = new List <char>(); while (!board.Score.Other.Wins) { board.Write(); Console.WriteLine(); if (!skipped) { rack = ReadRack(pending, board.Current); } skipped = false; PlayPath move = ReadMove(board, rack); if (move == null) { board = board.Skip(); skipped = true; continue; } using (PlayerScore.GetCurrentColor(board.Current)) move.Write(); board = board.Play(move); pending.Clear(); pending.AddRange(move.Pending); bool vortex = false; foreach (LetterPlay lp in move.Played) { vortex |= lp.Cell.IsVortex; } if (vortex) { board.Write(); Console.WriteLine(); board = board.Clear(); pending.Clear(); } } board.Write(); rack = null; using (new DisposableColor(PlayerScore.GetColor(board.Other))) Console.WriteLine("{0} wins", board.Other); Console.WriteLine(); } catch (GiveUpException) { if (board.IsEmpty && rack == null) { return; } rack = null; } board = new Board(); } }
public void Play() { var board = new Board(); var rack = new Rack(graph.GetRandom()); Console.WriteLine("rack: " + rack.Value); WordPart part = GetFirstWord(rack); Console.WriteLine("word: {0}", part); board = board.Play(part); var letters = new List <char>(rack.Letters); foreach (char c in part.Word) { letters.Remove(c); } board.Write(); while (!board.Score.Other.Wins) { var time = new Chrono(); Console.Write("------------------------------\n"); rack = new Rack(graph.GetRandom(new string(letters.ToArray()))); Console.WriteLine("rack: " + rack.Value); if (board.IsEmpty) { part = GetFirstWord(rack); Console.WriteLine("word: {0}", part); board = board.Play(part); letters = new List <char>(rack.Letters); foreach (char c in part.Word) { letters.Remove(c); } board.Write(); } else { var chrono = new Chrono(); PlayGraph play = GetPlayGraph(board, rack); double seconds = .1 * Convert.ToInt32(chrono.Elapsed.TotalSeconds * 10); Console.WriteLine("{0} moves in {1} s", play.Valids.Count, seconds); chrono = new Chrono(); var moveFinder = new MoveFinder(graph, board, play); Tuple <PlayInfo, PlayPath> best = moveFinder.GetBestMove(); seconds = .1 * Convert.ToInt32(chrono.Elapsed.TotalSeconds * 10); Console.WriteLine("Analyzed {0} moves in {1} s", play.Valids.Count, seconds); Console.WriteLine(); { PlayInfo info = best.Item1; PlayPath path = best.Item2; path.Write(); board = board.Play(path); board.Write(); if (info.HasVortex) { board = board.Clear(); letters.Clear(); } else { letters = new List <char>(rack.Letters); foreach (LetterPlay lp in path.Played) { letters.Remove(lp.Letter); } } } if (time.Elapsed.TotalSeconds > 40) { throw new TimeoutException(); } } } }