-
-
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!!" |
I think @supernova-at will appreciate the usage of bro.
Yeah my bias top row. To get around a bias I would continue validating the string and check for things like two winners and if 'x' or 'o' have the correct amount of turns. I would add a new state for this called "Invalid game, learn how to play bro"
@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.
@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!
Haha! You allowed for extreme Terranaova tic-tac-toe in yours by generalising the algorithm for bigger grids :)
I don't know anything much about Swift apart from ๐ฑ ๐ถ - but I get where you are going with this.
If this were for a client you had an idea might change their mind from the 9 grid spec.
"The size of the sequence needs to be a perfect square bro!!!" ๐
My answer had an x win bias on extreme tic-tac-toes, I think yours has a top row bias. I think both are fair considering that is unspecified ๐
e.g. "oooxxx---" I think o would win on yours and x would win on mine.