{"id":18425722,"url":"https://github.com/dwayne/xo","last_synced_at":"2025-10-17T16:08:14.954Z","repository":{"id":5820778,"uuid":"7036070","full_name":"dwayne/xo","owner":"dwayne","description":"A Ruby library for Tic-tac-toe.","archived":false,"fork":false,"pushed_at":"2014-05-23T07:46:27.000Z","size":560,"stargazers_count":6,"open_issues_count":1,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-22T21:23:41.850Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://rubydoc.info/github/dwayne/xo","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dwayne.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2012-12-06T13:29:09.000Z","updated_at":"2022-01-07T21:35:44.000Z","dependencies_parsed_at":"2022-08-31T18:50:39.395Z","dependency_job_id":null,"html_url":"https://github.com/dwayne/xo","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwayne%2Fxo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwayne%2Fxo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwayne%2Fxo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwayne%2Fxo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dwayne","download_url":"https://codeload.github.com/dwayne/xo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247688008,"owners_count":20979580,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-06T05:05:02.993Z","updated_at":"2025-10-17T16:08:14.858Z","avatar_url":"https://github.com/dwayne.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# xo\n\n[![Gem Version](https://badge.fury.io/rb/xo.svg)](http://badge.fury.io/rb/xo)\n[![Build Status](https://travis-ci.org/dwayne/xo.svg?branch=master)](https://travis-ci.org/dwayne/xo)\n[![Coverage Status](https://coveralls.io/repos/dwayne/xo/badge.png)](https://coveralls.io/r/dwayne/xo)\n[![Code Climate](https://codeclimate.com/github/dwayne/xo.png)](https://codeclimate.com/github/dwayne/xo)\n\nA [Ruby](http://www.ruby-lang.org/en/) library for\n[Tic-tac-toe](http://en.wikipedia.org/wiki/Tic-tac-toe).\n\nThe code is well documented and fully tested, so please have a\n[read of the documentation](http://rubydoc.info/github/dwayne/xo) and have a\n[look at the tests](https://github.com/dwayne/xo/tree/master/spec/xo).\n\nMy implementation of the [Minimax algorithm](http://en.wikipedia.org/wiki/Minimax#Minimax_algorithm_with_alternate_moves)\nmight be a little different than what you've seen before. It uses symmetry to\nsignificantly reduce the search space and in so doing we get good\n[performance out of the algorithm](#performance-of-the-minimax-algorithm).\nHowever, I still want to get it under 1 second. I'd love to hear your thoughts\non how I can make it happen. Have a\n[look](https://github.com/dwayne/xo/blob/master/lib/xo/ai/minimax.rb#L23).\n\n## Installation\n\n```\n$ gem install xo\n```\n\n## Example usage\n\nManaging the grid yourself.\n\n```ruby\nrequire 'xo'\ninclude XO\n\ng = Grid.new('xx oo')\n\nputs g # =\u003e  x | x |\n       #    ---+---+---\n       #     o | o |\n       #    ---+---+---\n       #       |   |\n\nevaluator = Evaluator.instance\n\nevaluator.analyze(g, Grid::X) # =\u003e { status: :ok }\n\ng[1, 3] = Grid::X\nevaluator.analyze(g, Grid::X) # =\u003e { status: :game_over,\n                              #      type: :winner,\n                              #      details: [{\n                              #        where: :row,\n                              #        index: 1,\n                              #        positions: [[1, 1], [1, 2], [1, 3]]\n                              #      }]\n                              #    }\n\nevaluator.analyze(g, Grid::O) # =\u003e { status: :game_over,\n                              #      type: :loser,\n                              #      details: [{\n                              #        where: :row,\n                              #        index: 1,\n                              #        positions: [[1, 1], [1, 2], [1, 3]]\n                              #      }]\n                              #    }\n```\n\nThe problem with managing the grid yourself is that there is nothing stopping\nyou from making bad moves. For example, playing twice.\n\n```ruby\ng = Grid.new('xx')\nevaluator.analyze(g, Grid::O) # =\u003e { status: :invalid_grid,\n                              #      type: :too_many_moves_ahead\n                              #    }\n```\n\nTo avoid such situations, let the engine handle game play. Once you tell it who\nplays first, then it ensures that the game play follows the rules of\n[Tic-tac-toe](http://en.wikipedia.org/wiki/Tic-tac-toe).\n\n```ruby\ne = Engine.new\n\nclass EngineObserver\n\n  attr_reader :last_event\n\n  def update(event)\n    @last_event = event\n  end\nend\n\nobserver = EngineObserver.new\ne.add_observer(observer)\n\ne.start(Grid::O).play(2, 1).play(1, 1).play(2, 2).play(1, 2).play(2, 3)\nobserver.last_event # =\u003e { name: :game_over,\n                    #      source: #\u003cXO::Engine...\u003e\n                    #      type: :winner,\n                    #      last_move: { turn: :o, r: 2, c: 3 },\n                    #      details: [{\n                    #        where: :row,\n                    #        index: 2,\n                    #        positions: [[2, 1], [2, 2], [2, 3]]\n                    #      }]\n                    #    }\n```\n\nI also built a [Tic-tac-toe](http://en.wikipedia.org/wiki/Tic-tac-toe)\ncommand-line client that uses the library. See how everything comes together by\nviewing its implementation right\n[here](https://github.com/dwayne/xo/blob/master/bin/xo).\n\nTo use the [client](https://github.com/dwayne/xo/blob/master/bin/xo) just type,\n\n```\n$ xo\n```\n\non the command-line after installing the gem.\n\n## Performance of the Minimax Algorithm\n\n```ruby\nrequire 'benchmark'\nrequire 'xo/ai/minimax'\n\nputs Benchmark.measure { XO::AI::Minimax.instance }\n# =\u003e 3.090000   0.000000   3.090000 (  3.091686)\n```\n\n## Testing\n\nYou can run:\n\n- All specs: `bundle exec rake`, or\n- A specific spec: `bundle exec ruby -Ilib -Ispec spec/path_to_spec_file.rb`\n\n## Contributing\n\nIf you'd like to contribute a feature or bugfix: Thanks! To make sure your\nfix/feature has a high chance of being included, please read the following\nguidelines:\n\n1. Post a [pull request](https://github.com/dwayne/xo/compare/).\n2. Make sure there are tests! I will not accept any patch that is not tested.\n   It's a rare time when explicit tests aren't needed. If you have questions\n   about writing tests for xo, please open a\n   [GitHub issue](https://github.com/dwayne/xo/issues/new).\n\n## License\n\nxo is Copyright © 2014 Dwayne R. Crooks. It is free software, and may be\nredistributed under the terms specified in the MIT-LICENSE file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdwayne%2Fxo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdwayne%2Fxo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdwayne%2Fxo/lists"}