Skip to content

Instantly share code, notes, and snippets.

@kmckinley
Last active August 29, 2015 14:27
Show Gist options
  • Save kmckinley/33dc47f4e682b4e375aa to your computer and use it in GitHub Desktop.
Save kmckinley/33dc47f4e682b4e375aa to your computer and use it in GitHub Desktop.
/*
Unlimited Tic Tac Toe!
This algorithm determines if a game has a winner, in progress, or finished with a tied game.
There is no limit to the size of the game as long as the size is a perfect square root e.g., 4,9,16,25,ect
*/
class TicTacToe {
let wins = "win!!"
func start(sequence: String) -> String {
let charSize = count(sequence)
let testGameSize = Double(charSize)%sqrt(Double(charSize))
if testGameSize != 0 {
return "The size of the sequence needs to be a perfect square bro!!!"
}else {
return gameStatus(sequence)
}
}
func gameStatus(sequence: String) -> String {
// convert string into a char array
let charArray = Array(sequence)
// Determine the divisor inorder to create an algorithm that doesn't rely on
// an explicilty defined data structure.
let divisor = Int(sqrt(Double(charArray.count)))
for var i=0; i<charArray.count; ++i {
let char = charArray[i]
if i < divisor {
// First check first row, all columns and all diagonals
if i == 0 {
// Top row, first column, first diagonal
if rowTest(charArray, index: i, divisor: divisor) {
return "\(char) \(wins)"
}
if columnTest(charArray, index: i, divisor: divisor) {
return "\(char) \(wins)"
}
if diagonalTest(charArray, index: i, conditionNum: charArray.count, increment: divisor+1) {
return "\(char) \(wins)"
}
}else if i == divisor-1 {
// Last column, second diagonal
if columnTest(charArray, index: i, divisor: divisor) {
return "\(char) \(wins)"
}
// I don't like how I came up with the conditionNum, but I couldn't think of anything else
// If I don't subtract 2 from the array count it will check the last position of the char
// array. This is not an issue when checking the other diagonal.
if diagonalTest(charArray, index: i, conditionNum: charArray.count-2, increment: divisor-1) {
return "\(char) \(wins)"
}
}else {
// All columns other than the first and last
if columnTest(charArray, index: i, divisor: divisor) {
return "\(char) \(wins)"
}
}
} else if ((i%divisor) == 0) {
// All remaining rows
if rowTest(charArray, index: i, divisor: divisor) {
return "\(char) \(wins)"
}
}
}
if sequence.rangeOfString("-") != nil {
return "Game still in progress"
}else {
return "Tie Game"
}
}
// Test the entire row based on the divisor and index position. Only call this method
// when you are at the first column of a row.
func rowTest(charArray: Array<Character>, index: Int, divisor: Int) -> Bool {
let char = charArray[index]
var match = true
for var j=index; j<divisor+index && match; ++j {
if char == "-" || char != charArray[j] {
match = false
}
}
return match
}
// Test the entire column based on the divisor and index position. Only call this method
// when you are at the first row of a column.
func columnTest(charArray: Array<Character>, index: Int, divisor: Int) -> Bool {
let char = charArray[index]
var match = true
for var j=index; j<charArray.count && match; j += divisor {
if char == "-" || char != charArray[j] {
match = false
}
}
return match
}
// Test diagonal line. Only called when at first column of first row, or last column of first row.
// conditionNum is used to ensure that we don't loop through the last position of the charArray when
// we are testing the diagonal from the last column first row
// increment is different depending on which diagonal we are testing.
func diagonalTest(charArray: Array<Character>, index: Int, conditionNum: Int, increment: Int) -> Bool {
let char = charArray[index]
var match = true
for var j=index; j<=conditionNum && match; j += increment {
if char == "-" || char != charArray[j] {
match = false
}
}
return match
}
}
// Lets see it in action
let ttt = TicTacToe()
ttt.start("xxx-o-o-o") // "x wins!!"
ttt.start("xo-ox-o-x") // "x wins!!"
ttt.start("x-o-o-oxx") // "o wins!!"
ttt.start("x-xooo-x-") // "o wins!!"
ttt.start("ooxxxooxx") // "Tie Game"
ttt.start("oxooxoxox") // "Tie Game"
ttt.start("---------") // "Game still in progress"
ttt.start("xox---oxo") // "Game still in progress"
ttt.start("----------------") // "Game still in progress"
ttt.start("x----x----x----x") // "x wins!!"
ttt.start("-o---o---o---o--") // "o wins!!"
ttt.start("---------------------------------------------------------------------------------"); // "Game still in progress"
ttt.start("------x--------x--------x--------x--------x--------x--------x--------x--------x--"); // "x wins!!"
ttt.start("--------x-------x-------x-------x-------x-------x-------x-------x-------x--------"); // "x wins!!"
ttt.start("------------------------------------------------------------------------ooooooooo"); // "o wins!!"
ttt.start("---------------------------------------------------------------ooooooooo---------"); // "o wins!!"
@paulevans
Copy link

@kmckinley yeah... I put in comments directed at @supernova-at myself 😄

Invalid games weren't specified and would complicate things a bit so I left it out too. As you say I think correct number of turns plus checking for two winners would do it.

@aterranova-bv
Copy link

@kmckinley - I did get a good chuckle at it 👍

I had to stop and think about the loop code for a second to reason about it in my head, so perhaps my only comment would be to add more instructive comments. I'm coming at it from the perspective of you want to make it as easy as possible for someone who has never seen your code before to come in, find, and fix a potential bug.

I like the extrapolation out to any grid size!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment