I am trying to use a stack-based approach to parse a bencoded string.
This link describes bencoding: https://www.bittorrent.org/beps/bep_0003.html
My psuedocode fails to handle the case where there are nested lists, for example, [1, [2]], and [[1, 2]] will both return [[1 ,2]], even when clearly the encoding is different, "li1eli2eee" versus "lli1ei2eee".
Here is my psuedocode thus far
input: string
output: map/list/integer/string in a bencoded data structure
first, tokenize the string into valid tokens
Valid tokens "d, l, [words], [numbers], e, s (virtual token)"
Strings are tokenized as 4:spam becomes "s spam e" with s being a virtual token
Eg. li1el4:spamee becomes [l i 1 e i 22 e l s spam e i 2 e e e]
Parsing:
make two stacks:
stack1
stack2
for token in tokens:
if stack is empty
return error
if the token isn’t an “e”
push token onto stack1
while the stack isn’t empty:
elem = pop off the stack
if elem is “i”
elem2 = pop elem off stack2 and check if it can be converted to an int
if not
return error
push elem2 onto stack2 again
elif elem is “d”
make a new dict
while stack2 isn’t empty:
key = pop off stack2
if stack2 is empty:
return error (because then we have an odd key value encoding)
value = pop off stack2
dict[key] = value
push dict onto stack2
elif elem is “l”
make a new list
while stack2 isn’t empty:
append pop off stack2 to l
push l onto stack2
elif elem is “s”
dont need to do anything :P
else
push elem onto stack2
if stack2 isn’t empty:
ret = pop the lone element off stack2
if stack2 isn’t empty:
return error
return ret
I don't quite follow the spec or the pseudocode, but it seems pretty straightforward to implement a subset of "Bencoding" to handle the two strings you've shown (lists and integers). Everything else is relatively trivial (dicts are the same as lists, more or less, and strings and other non-recursively defined data types are basically the same as ints) as far as I can tell.
My algorithm is as follows:
Make a stack and put an empty array into it.
For each index in the bencoded string:
If the current character is i, parse the integer and fast-forward the index to the e that closes the integer. Append the integer to the array at the top of the stack.
If the current character is l, push a new arr onto the stack.
If the current character is e, pop the stack and push the popped array onto the array below it (i.e. the new top).
Return the only element in the stack.
Here it is in JS:
const tinyBencodeDecode = s => {
const stack = [[]];
for (let i = 0; i < s.length; i++) {
if (s[i] === "i") {
for (var j = ++i; s[j] !== "e"; j++);
stack[stack.length-1].push(+s.slice(i, j));
i = j;
}
else if (s[i] === "l") {
stack.push([]);
}
else if (s[i] === "e") {
stack[stack.length-2].push(stack.pop());
}
}
return stack[0];
};
[
"i1ei2e", // => [1, 2]
"lli1ei2eee", // => [[1, 2]]
"li1eli2eee", // => [[1, [2]]]
// [44, [1, [23, 561, [], 1, [78]]], 4]
"i44eli1eli23ei561elei1eli78eeeei4e",
].forEach(e => console.log(JSON.stringify(tinyBencodeDecode(e))));
No error handling is performed and everything is assumed to be well-formed, but error handling doesn't impact the fundamental algorithm; it's just a matter of adding a bunch of conditionals to check the index, stack and string as you work.
Here's an (admittedly lazy) example of how you could support the 4 datatypes. Again, error handling is omitted. The idea is basically the same as above except more fussing is needed to determine whether we're building a dictionary or a list. Since null doesn't appear to be a valid key per the spec, I'm using it a placeholder to pair up value tokens with their corresponding key.
In both cases, minor adjustments will need to bee made if it turns out that bencoding only has a single root element (list or dictionary). In that case, s = "i42ei43e" would be invalid on the top level and we'd start with an empty stack.
const back = (a, n=1) => a[a.length-n];
const append = (stack, data) => {
if (Array.isArray(back(stack))) {
back(stack).push(data);
}
else {
const emptyKey = Object.entries(back(stack))
.find(([k, v]) => v === null);
if (emptyKey) {
back(stack)[emptyKey[0]] = data;
}
else {
back(stack)[data] = null;
}
}
};
const bencodeDecode = s => {
const stack = [[]];
for (let i = 0; i < s.length; i++) {
if (s[i] === "i") {
for (var j = ++i; s[j] !== "e"; j++);
append(stack, +s.slice(i, j));
i = j;
}
else if (/\d/.test(s[i])) {
for (var j = i; s[j] !== ":"; j++);
const num = +s.slice(i, j++);
append(stack, s.slice(j, j + num));
i = j + num - 1;
}
else if (s[i] === "l") {
stack.push([]);
}
else if (s[i] === "d") {
stack.push({});
}
else if (s[i] === "e") {
append(stack, stack.pop());
}
}
return stack[0];
};
[
"i1ei2e", // => [1, 2]
"lli1ei2eee", // => [[1, 2]]
"li1eli2eee", // => [[1, [2]]]
"li1e4:spamli2eee", // => [[1, "spam", [2]]]
// [[1, "spam", {"cow": "moo", "spam": {"eggs": [6, "rice"]}}, [2]]]
"li1e4:spamd3:cow3:moo4:spamd4:eggsli6e4:riceeeeli2eee",
// [44, [1, [23, 561, [], 1, [78]]], 4]
"i44eli1eli23ei561elei1eli78eeeei4e",
].forEach(e => console.log(JSON.stringify(bencodeDecode(e))));
I need a data structure which is a list of integers but each time an integer is added to it the value it stores is the sum of the values it contains plus the value that is added.
For example:
def incrementingList = []
incrementingList.add(5) // now it has 5
incrementingList.add(3) // now it has 5,8
incrementingList.add(2) // now it has 5,8,10
Is there a groovy way to implement this so it can be ready to use as in the example?
UPDATE
What if it is possible for this list to contain 0s and if the last element is a 0 then it should increment by the last non 0 element?
You can use metaprogramming to define custom method:
List.metaClass.plusAdd = { e ->
delegate.isEmpty() ? delegate.add(e) : delegate.add(delegate.last() + e)
}
def l = []
l.plusAdd(5)
l.plusAdd(3)
l.plusAdd(2)
assert l == [5, 8, 10]
EDIT
Update for adding last non-zero element:
List.metaClass.plusAdd = { e ->
if(delegate.isEmpty()) {
delegate << e
} else {
def nonZeros = delegate.findAll { it > 0 }
delegate << (nonZeros ? nonZeros.last() + e : e)
}
}
def l = []
l.plusAdd(5)
l.plusAdd(3)
l.plusAdd(2)
assert l == [5, 8, 10]
l = [5, 0]
l.plusAdd(5)
assert l == [5, 0, 10]
l = [1,0]
l.plusAdd(5)
assert l == [1, 0 ,6]
this should be simple and working
incrementingList.add(incrementingList.last() + 5)
check if not empty:
incrementingList.add((incrementingList.size() > 0 ) ? incrementingList.last() + 5 : 5)
,
def incrementingList = []
incrementingList.add((incrementingList.size() > 0 ) ? incrementingList.last() + 5 : 5)
incrementingList.add((incrementingList.size() > 0 ) ? incrementingList.last() + 5 : 5)
incrementingList.add((incrementingList.size() > 0 ) ? incrementingList.last() + 5 : 5)
println incrementingList
output:
[5, 10, 15]
Slightly different approach:
def addToList(incList, val) {
if (incList.size() > 0) {
incList << incList.last() + val
} else {
incList << val
}
return incList
}
def incList = []
addToList(incList, 3)
addToList(incList, 2)
addToList(incList, 5)
println incList
output:
[3, 5, 10]
Updated:
def addToList(incList, val) {
if (incList.size() > 0) {
if (val != 0) {
incList << val + incList.last()
} else {
def addVal = incList.size() > 1 ? (incList[-1] - incList[-2]) : incList[-1]
incList << addVal + incList.last()
}
} else {
incList << val
}
}
def incList = []
addToList(incList, 5)
addToList(incList, 3)
addToList(incList, 0)
addToList(incList, 5)
addToList(incList, 0)
println incList
output:
[5, 8, 11, 16, 21]
Suggestion, based on the update request (as I understand it)...
[Edit: the 'empty' case is covered by the 'no non-zero' case, so it can be simplified]
List.metaClass.addAsSum = { e ->
def nonZero = delegate.reverse().find { it != 0 }
if (nonZero != null) {
delegate.add(nonZero + e)
} else {
delegate.add(e)
}
}
Test runs:
def list
// test 0
list = []
list.addAsSum(5)
assert list == [5]
// test 1
list = []
list.addAsSum(5)
list.addAsSum(3)
list.addAsSum(2)
assert list == [5, 8, 10]
// test 2
list = []
list.addAsSum(5)
list.addAsSum(3)
list.addAsSum(0)
list.addAsSum(2)
assert list == [5, 8, 8, 10]
// test 3
list = []
list.addAsSum(0)
list.addAsSum(0)
list.addAsSum(0)
list.addAsSum(2)
list.addAsSum(4)
assert list == [0, 0, 0, 2, 6]
Adding a requirement that on a list filled only with zeros it should simply add the new element, this is my suggestion:
List.metaClass.fill = { n ->
delegate ? delegate.add((delegate.reverse().find { it > 0 } ?: 0) + n) : delegate.add(n)
delegate
}
assert [].fill(1).fill(2).fill(3) == [1, 3, 6]
assert [5, 0].fill(5) == [5, 0, 10]
assert [0, 0].fill(5).fill(5) == [0, 0, 5, 10]
note that I return delegate in the fill() such that you can call it in sequence.
I am trying to solve a problem.
Problem :
You are given a sequence of N balls in 4 colors: red, green, yellow and blue. The sequence is full of colors if and only if all of the following conditions are true:
There are as many red balls as green balls.
There are as many yellow balls as blue balls.
Difference between the number of red balls and green balls in every prefix of the sequence is at most 1.
Difference between the number of yellow balls and blue balls in every prefix of the sequence is at most 1.
Your task is to write a program, which for a given sequence prints True if it is full of colors, otherwise it prints False.
My solution : for each string, i am generating all possible prefixes and suffixes to validate the condition number 3 and 4. But it is taking more time.
instead of generating prefix and validating conditions every time, we can iterate over the string and validate the condition. I want to break out of loop when condition is not met. I am not able to get that in functional style. Can someone help me how to achieve it.
My solution :
object Test {
def main(args: Array[String]) {
def isValidSequence(str: String) = {
def isValidCondition(ch1:Char, ch2:Char, m:Map[Char, Int]):Boolean = m.getOrElse(ch1, 0) - m.getOrElse(ch2, 0) > 1
def groupByChars(s:String) = s.groupBy(ch => ch).map(x => (x._1, x._2.length))
def isValidPrefix(s:String):Boolean = (1 to s.length).exists(x => isValidCondition('R', 'G', groupByChars(s.take(x))))
val x = groupByChars(str)
lazy val cond1 = x.get('R') == x.get('G')
lazy val cond2 = x.get('B') == x.get('Y')
lazy val cond3 = isValidPrefix(str)
lazy val cond4 = isValidPrefix(str.reverse)
cond1 && cond2 && !cond3 && !cond4
}
def printBoolValue(b:Boolean) = if(b) println("True") else println("False")
val in = io.Source.stdin.getLines()
val inSize = in.take(1).next().toInt
val strs = in.take(inSize)
strs.map(isValidSequence(_)).foreach(printBoolValue)
}
}
As another answer, here's a more straightforward solution, that does short-circuit the differences check.
val valid = List("RGYBRGYB")
val invalid = List("RGYBR", "RGYBY", "RGYBY", "RGYYB")
def checkBalls(s:String) = {
def differences(s:String, a:Char, b:Char) = {
def differenceHelp(s:String, a:Char, b:Char, current:Int):Boolean = {
if (current < -1 || current > 1) false
else if (s.length == 0) true
else differenceHelp(s.tail, a, b,
if (s.head == a) current + 1 else if (s.head == b) current - 1 else current)
}
differenceHelp(s, a, b, 0)
}
lazy val cond1 = s.count('R'==) == s.count('G'==)
lazy val cond2 = s.count('Y'==) == s.count('B'==)
lazy val cond3 = differences(s, 'R', 'G')
lazy val cond4 = differences(s, 'Y', 'B')
cond1 && cond2 && cond3 && cond4
}
valid.forall(checkBalls(_)) //> res0: Boolean = true
invalid.forall(!checkBalls(_)) //> res1: Boolean = true
EDIT: as an optimisation, we can do cond1 as part of cond3 (and cond2 as part of cond4). There are equal numbers of each if and only if the count is 0 at the end of the string. We can check that in differences and return true only if that's the case. So that gives
def checkBalls(s:String) = {
def differences(s:String, a:Char, b:Char) = {
def differenceHelp(s:String, a:Char, b:Char, current:Int):Boolean = {
if (current < -1 || current > 1) false
else if (s.length == 0) (count == 0) // <- this line changed
else differenceHelp(s.tail, a, b,
if (s.head == a) current + 1 else if (s.head == b) current - 1 else current)
}
differenceHelp(s, a, b, 0)
}
lazy val cond3 = differences(s, 'R', 'G')
lazy val cond4 = differences(s, 'Y', 'B')
cond3 && cond4
}
which passes the tests just like the previous version. It could be made slightly faster by doing the R/G and Y/B checks in one call to differences, but that's looking a bit overspecialised.
Here is a solution using streams if you need.
code :-
object RGYB extends App {
val validPattern = List(
"RG","RYBG","RYGB","RBGY",
"GR","GYBR","GYRB","GBRY",
"YB","YRGB","YRBG","YGRB",
"BY","BRGY","BRYG","BGYR"
)
val pattern ="RGRG"
pattern.sliding(4).foreach { x1 =>
val count = validPattern.filter { p1 => {
x1.equalsIgnoreCase(p1)
}
}.size
if(count<1)
{
x1.sliding(2).foreach {
x2=>
val counter = validPattern.filter { p2 => {
x2.equalsIgnoreCase(p2)
}
}.size
if(counter<1)
{
println("false !! not valid due to "+x2);
System.exit(0)
}
}
println("false !! not valid due to "+x1);
System.exit(0)
}
}
println("True !!"+pattern+" Is a valid string pattern")
}
So, the trick is to check the longest prefix first. If that fails, we're done. Otherwise, we take the next longest prefix and recurse. If we get to the empty string, it passed for all prefixes, and therefore it's valid.
def isValidPrefix(s: String): Boolean =
if (s.length == 0)
true
else if (!isValidCondition('R', 'G', groupByChars(s)))
false
else isValidPrefix(s.init)
I had an interview were I was asked a seemingly simple algorithm question: "Write an algorithm to return me all possible winning combinations for tic tac toe." I still can't figure out an efficient way to handle this. Is there a standard algorithm or common that should be applied to similar questions like this that I'm not aware of?
This is one of those problems that's actually simple enough for brute force and, while you could use combinatorics, graph theory, or many other complex tools to solve it, I'd actually be impressed by applicants that recognise the fact there's an easier way (at least for this problem).
There are only 39, or 19,683 possible combinations of placing x, o or <blank> in the grid, and not all of those are valid.
First, a valid game position is one where the difference between x and o counts is no more than one, since they have to alternate moves.
In addition, it's impossible to have a state where both sides have three in a row, so they can be discounted as well. If both have three in a row, then one of them would have won in the previous move.
There's actually another limitation in that it's impossible for one side to have won in two different ways without a common cell (again, they would have won in a previous move), meaning that:
XXX
OOO
XXX
cannot be achieved, while:
XXX
OOX
OOX
can be. But we can actually ignore that since there's no way to win two ways without a common cell without having already violated the "maximum difference of one" rule, since you need six cells for that, with the opponent only having three.
So I would simply use brute force and, for each position where the difference is zero or one between the counts, check the eight winning possibilities for both sides. Assuming only one of them has a win, that's a legal, winning game.
Below is a proof of concept in Python, but first the output of time when run on the process sending output to /dev/null to show how fast it is:
real 0m0.169s
user 0m0.109s
sys 0m0.030s
The code:
def won(c, n):
if c[0] == n and c[1] == n and c[2] == n: return 1
if c[3] == n and c[4] == n and c[5] == n: return 1
if c[6] == n and c[7] == n and c[8] == n: return 1
if c[0] == n and c[3] == n and c[6] == n: return 1
if c[1] == n and c[4] == n and c[7] == n: return 1
if c[2] == n and c[5] == n and c[8] == n: return 1
if c[0] == n and c[4] == n and c[8] == n: return 1
if c[2] == n and c[4] == n and c[6] == n: return 1
return 0
pc = [' ', 'x', 'o']
c = [0] * 9
for c[0] in range (3):
for c[1] in range (3):
for c[2] in range (3):
for c[3] in range (3):
for c[4] in range (3):
for c[5] in range (3):
for c[6] in range (3):
for c[7] in range (3):
for c[8] in range (3):
countx = sum([1 for x in c if x == 1])
county = sum([1 for x in c if x == 2])
if abs(countx-county) < 2:
if won(c,1) + won(c,2) == 1:
print " %s | %s | %s" % (pc[c[0]],pc[c[1]],pc[c[2]])
print "---+---+---"
print " %s | %s | %s" % (pc[c[3]],pc[c[4]],pc[c[5]])
print "---+---+---"
print " %s | %s | %s" % (pc[c[6]],pc[c[7]],pc[c[8]])
print
As one commenter has pointed out, there is one more restriction. The winner for a given board cannot have less cells than the loser since that means the loser just moved, despite the fact the winner had already won on the last move.
I won't change the code to take that into account but it would be a simple matter of checking who has the most cells (the last person that moved) and ensuring the winning line belonged to them.
Another way could be to start with each of the eight winning positions,
xxx ---
--- xxx
--- --- ... etc.,
and recursively fill in all legal combinations (start with inserting 2 o's, then add an x for each o ; avoid o winning positions):
xxx xxx xxx
oo- oox oox
--- o-- oox ... etc.,
Today I had an interview with Apple and I had the same question. I couldn't think well at that moment. Later one on, before going to a meeting I wrote the function for the combinations in 15 minutes, and when I came back from the meeting I wrote the validation function again in 15 minutes. I get nervous at interviews, Apple not trusts my resume, they only trust what they see in the interview, I don't blame them, many companies are the same, I just say that something in this hiring process doesn't look quite smart.
Anyways, here is my solution in Swift 4, there are 8 lines of code for the combinations function and 17 lines of code to check a valid board.
Cheers!!!
// Not used yet: 0
// Used with x : 1
// Used with 0 : 2
// 8 lines code to get the next combination
func increment ( _ list: inout [Int], _ base: Int ) -> Bool {
for digit in 0..<list.count {
list[digit] += 1
if list[digit] < base { return true }
list[digit] = 0
}
return false
}
let incrementTicTacToe = { increment(&$0, 3) }
let win0_ = [0,1,2] // [1,1,1,0,0,0,0,0,0]
let win1_ = [3,4,5] // [0,0,0,1,1,1,0,0,0]
let win2_ = [6,7,8] // [0,0,0,0,0,0,1,1,1]
let win_0 = [0,3,6] // [1,0,0,1,0,0,1,0,0]
let win_1 = [1,4,7] // [0,1,0,0,1,0,0,1,0]
let win_2 = [2,5,8] // [0,0,1,0,0,1,0,0,1]
let win00 = [0,4,8] // [1,0,0,0,1,0,0,0,1]
let win11 = [2,4,6] // [0,0,1,0,1,0,1,0,0]
let winList = [ win0_, win1_, win2_, win_0, win_1, win_2, win00, win11]
// 16 lines to check a valid board, wihtout countin lines of comment.
func winCombination (_ tictactoe: [Int]) -> Bool {
var count = 0
for win in winList {
if tictactoe[win[0]] == tictactoe[win[1]],
tictactoe[win[1]] == tictactoe[win[2]],
tictactoe[win[2]] != 0 {
// If the combination exist increment count by 1.
count += 1
}
if count == 2 {
return false
}
}
var indexes = Array(repeating:0, count:3)
for num in tictactoe { indexes[num] += 1 }
// '0' and 'X' must be used the same times or with a diference of one.
// Must one and only one valid combination
return abs(indexes[1] - indexes[2]) <= 1 && count == 1
}
// Test
var listToIncrement = Array(repeating:0, count:9)
var combinationsCount = 1
var winCount = 0
while incrementTicTacToe(&listToIncrement) {
if winCombination(listToIncrement) == true {
winCount += 1
}
combinationsCount += 1
}
print("There is \(combinationsCount) combinations including possible and impossible ones.")
print("There is \(winCount) combinations for wining positions.")
/*
There are 19683 combinations including possible and impossible ones.
There are 2032 combinations for winning positions.
*/
listToIncrement = Array(repeating:0, count:9)
var listOfIncremented = ""
for _ in 0..<1000 { // Win combinations for the first 1000 combinations
_ = incrementTicTacToe(&listToIncrement)
if winCombination(listToIncrement) == true {
listOfIncremented += ", \(listToIncrement)"
}
}
print("List of combinations: \(listOfIncremented)")
/*
List of combinations: , [2, 2, 2, 1, 1, 0, 0, 0, 0], [1, 1, 1, 2, 2, 0, 0, 0, 0],
[2, 2, 2, 1, 0, 1, 0, 0, 0], [2, 2, 2, 0, 1, 1, 0, 0, 0], [2, 2, 0, 1, 1, 1, 0, 0, 0],
[2, 0, 2, 1, 1, 1, 0, 0, 0], [0, 2, 2, 1, 1, 1, 0, 0, 0], [1, 1, 1, 2, 0, 2, 0, 0, 0],
[1, 1, 1, 0, 2, 2, 0, 0, 0], [1, 1, 0, 2, 2, 2, 0, 0, 0], [1, 0, 1, 2, 2, 2, 0, 0, 0],
[0, 1, 1, 2, 2, 2, 0, 0, 0], [1, 2, 2, 1, 0, 0, 1, 0, 0], [2, 2, 2, 1, 0, 0, 1, 0, 0],
[2, 2, 1, 0, 1, 0, 1, 0, 0], [2, 2, 2, 0, 1, 0, 1, 0, 0], [2, 2, 2, 1, 1, 0, 1, 0, 0],
[2, 0, 1, 2, 1, 0, 1, 0, 0], [0, 2, 1, 2, 1, 0, 1, 0, 0], [2, 2, 1, 2, 1, 0, 1, 0, 0],
[1, 2, 0, 1, 2, 0, 1, 0, 0], [1, 0, 2, 1, 2, 0, 1, 0, 0], [1, 2, 2, 1, 2, 0, 1, 0, 0],
[2, 2, 2, 0, 0, 1, 1, 0, 0]
*/
This is a java equivalent code sample
package testit;
public class TicTacToe {
public static void main(String[] args) {
// TODO Auto-generated method stub
// 0 1 2
// 3 4 5
// 6 7 8
char[] pc = {' ' ,'o', 'x' };
char[] c = new char[9];
// initialize c
for (int i = 0; i < 9; i++)
c[i] = pc[0];
for (int i = 0; i < 3; i++) {
c[0] = pc[i];
for (int j = 0; j < 3; j++) {
c[1] = pc[j];
for (int k = 0; k < 3; k++) {
c[2] = pc[k];
for (int l = 0; l < 3; l++) {
c[3] = pc[l];
for (int m = 0; m < 3; m++) {
c[4] = pc[m];
for (int n = 0; n < 3; n++) {
c[5] = pc[n];
for (int o = 0; o < 3; o++) {
c[6] = pc[o];
for (int p = 0; p < 3; p++) {
c[7] = pc[p];
for (int q = 0; q < 3; q++) {
c[8] = pc[q];
int countx = 0;
int county = 0;
for(int r = 0 ; r<9 ; r++){
if(c[r] == 'x'){
countx = countx + 1;
}
else if(c[r] == 'o'){
county = county + 1;
}
}
if(Math.abs(countx - county) < 2){
if(won(c, pc[2])+won(c, pc[1]) == 1 ){
System.out.println(c[0] + " " + c[1] + " " + c[2]);
System.out.println(c[3] + " " + c[4] + " " + c[5]);
System.out.println(c[6] + " " + c[7] + " " + c[8]);
System.out.println("*******************************************");
}
}
}
}
}
}
}
}
}
}
}
}
public static int won(char[] c, char n) {
if ((c[0] == n) && (c[1] == n) && (c[2] == n))
return 1;
else if ((c[3] == n) && (c[4] == n) && (c[5] == n))
return 1;
else if ((c[6] == n) && (c[7] == n) && (c[8] == n))
return 1;
else if ((c[0] == n) && (c[3] == n) && (c[6] == n))
return 1;
else if ((c[1] == n) && (c[4] == n) && (c[7] == n))
return 1;
else if ((c[2] == n) && (c[5] == n) && (c[8] == n))
return 1;
else if ((c[0] == n) && (c[4] == n) && (c[8] == n))
return 1;
else if ((c[2] == n) && (c[4] == n) && (c[6] == n))
return 1;
else
return 0;
}
}
`
Below Solution generates all possible combinations using recursion
It has eliminated impossible combinations and returned 888 Combinations
Below is a working code Possible winning combinations of the TIC TAC TOE game
const players = ['X', 'O'];
let gameBoard = Array.from({ length: 9 });
const winningCombination = [
[ 0, 1, 2 ],
[ 3, 4, 5 ],
[ 6, 7, 8 ],
[ 0, 3, 6 ],
[ 1, 4, 7 ],
[ 2, 5, 8 ],
[ 0, 4, 8 ],
[ 2, 4, 6 ],
];
const isWinningCombination = (board)=> {
if((Math.abs(board.filter(a => a === players[0]).length -
board.filter(a => a === players[1]).length)) > 1) {
return false
}
let winningComb = 0;
players.forEach( player => {
winningCombination.forEach( combinations => {
if (combinations.every(combination => board[combination] === player )) {
winningComb++;
}
});
});
return winningComb === 1;
}
const getCombinations = (board) => {
let currentBoard = [...board];
const firstEmptySquare = board.indexOf(undefined)
if (firstEmptySquare === -1) {
return isWinningCombination(board) ? [board] : [];
} else {
return [...players, ''].reduce((prev, next) => {
currentBoard[firstEmptySquare] = next;
if(next !== '' && board.filter(a => a === next).length > (gameBoard.length / players.length)) {
return [...prev]
}
return [board, ...prev, ...getCombinations(currentBoard)]
}, [])
}
}
const startApp = () => {
let combination = getCombinations(gameBoard).filter(board =>
board.every(item => !(item === undefined)) && isWinningCombination(board)
)
printCombination(combination)
}
const printCombination = (combination)=> {
const ulElement = document.querySelector('.combinations');
combination.forEach(comb => {
let node = document.createElement("li");
let nodePre = document.createElement("pre");
let textnode = document.createTextNode(JSON.stringify(comb));
nodePre.appendChild(textnode);
node.appendChild(nodePre);
ulElement.appendChild(node);
})
}
startApp();
This discovers all possible combinations for tic tac toe (255,168) -- written in JavaScript using recursion. It is not optimized, but gets you what you need.
const [EMPTY, O, X] = [0, 4, 1]
let count = 0
let coordinate = [
EMPTY, EMPTY, EMPTY,
EMPTY, EMPTY, EMPTY,
EMPTY, EMPTY, EMPTY
]
function reducer(arr, sumOne, sumTwo = null) {
let func = arr.reduce((sum, a) => sum + a, 0)
if((func === sumOne) || (func === sumTwo)) return true
}
function checkResult() {
let [a1, a2, a3, b1, b2, b3, c1, c2, c3] = coordinate
if(reducer([a1,a2,a3], 3, 12)) return true
if(reducer([a1,b2,c3], 3, 12)) return true
if(reducer([b1,b2,b3], 3, 12)) return true
if(reducer([c1,c2,c3], 3, 12)) return true
if(reducer([a3,b2,c1], 3, 12)) return true
if(reducer([a1,b1,c1], 3, 12)) return true
if(reducer([a2,b2,c2], 3, 12)) return true
if(reducer([a3,b3,c3], 3, 12)) return true
if(reducer([a1,a2,a3,b1,b2,b3,c1,c2,c3], 21)) return true
return false
}
function nextPiece() {
let [countX, countO] = [0, 0]
for(let i = 0; i < coordinate.length; i++) {
if(coordinate[i] === X) countX++
if(coordinate[i] === O) countO++
}
return countX === countO ? X : O
}
function countGames() {
if (checkResult()) {
count++
}else {
for (let i = 0; i < 9; i++) {
if (coordinate[i] === EMPTY) {
coordinate[i] = nextPiece()
countGames()
coordinate[i] = EMPTY
}
}
}
}
countGames()
console.log(count)
I separated out the checkResult returns in case you want to output various win conditions.
Could be solved with brute force but keep in mind the corner cases like player2 can't move when player1 has won and vice versa. Also remember Difference between moves of player1 and player can't be greater than 1 and less than 0.
I have written code for validating whether provided combination is valid or not, might soon post on github.