Sorry for the vague title. Please edit or comment if you know of a better one.
Game description is below. I have a solution that works but coding it would be O($N!$) time complexity. I know there's a better solution that could be done with a single iteration (or two?).
My first attempt involved running through the entire game tree (a 3-ary tree) where the "left from starting position" and "right from last position" moves are disallowed. The height of the tree is $2^t+1$. The complexity of the tree (i.e. number of leaf nodes) provides the correct answer, but this approach is too computationally intensive at O($3^t$). I'm looking for a faster approach
My second attempt uses the fact that each game is a series of R/L/S moves (right, left, stay). Every valid game must have at least $n-1$ R moves to get from start to finish. The first valid game is thus a series of R's followed by $t-n+1$ S's. Every permutation of this sequence is a valid game.
e.g. for t = 5, n = 3 (5 moves on a board size 3), these are valid games: RRSSS, RSRRR, SRSRR, etc.
Thereafter we can iterate by removing 2 S's in the sequence and replacing them with an LR, e.g.: RLRRS, RRLRS, SRLRR, etc.
We iterate down until there are <= 1 S's remaining in the sequence.
Each game has $\frac {T!} {(R!L!S!)}$ possible games (valid and invalid). To remove the invalid ones we need to calculate how many of those games will have the token on the starting point and then move L, or the token at the end and then move R.
This is where my stats/combinatorics knowledge is broken. Can anyone help me to learn the math for finding the total number of invalid games given a sequence like above?
My sample code for this second approach is copied below. I'm just missing implementations of the two method to find the invalid games.
Game Description
The game is a single player game, played on a board with n squares in a horizontal row. The player places a token on the left-most square and rolls a special three-sided die.
If the die rolls a "Left", the player moves the token to a square one space to the left of where it is currently. If there is no square to the left, the game is invalid, and you start again.
If the die rolls a "Stay", the token stays where it is.
If the die rolls a "Right", the player moves the token to a square, one space to the right of where it is currently. If there is no square to the right, the game is invalid and you start again.
The aim is to roll the dice exactly t times, and be at the rightmost square on the last roll. If you land on the rightmost square before t rolls are done then the only valid dice roll is to roll a "Stay". If you roll anything else, the game is invalid (i.e., you cannot move left or right from the rightmost square).
To make it more interesting, players have leaderboards (one for each n,t pair) where each player submits the game he just played: the sequence of dice rolls. If some player has already submitted the exact same sequence, they cannot submit a new entry, so the entries in the leader-board correspond to unique games playable.
Since the players refresh the leaderboards frequently on their mobile devices, as an infiltrating hacker, you are interested in knowing the maximum possible size a leaderboard can have.
Write a function answer(t, n), which given the number of dice rolls t, and the number of squares in the board n, returns the possible number of unique games modulo 123454321. i.e. if the total number is S, then return the remainder upon dividing S by 123454321, the remainder should be an integer between 0 and 123454320 (inclusive).
n and t will be positive integers, no more than 1000. n will be at least 2.
Sample Code
import math
def answer(t, n):
# Min board size is 2
if n < 2:
return 0
# Need to have at least as many moves as needed to make it to the final square
if t < n - 1:
return 0
# Number of each type of move to start with.
# Always start with the simple case of enough R moves to get to the final square, then stay there
L = 0
R = n - 1
S = t - n + 1
t_fact = math.factorial(t)
t_less1_fact = math.factorial(t-1)
total = 0
count = math.ceil((t-n+1)/2) + 1
for i in range(0,count):
S = S - 2 * i
R += i
L += i
R_fact = math.factorial(R)
S_fact = math.factorial(S)
L_fact = math.factorial(L)
total += t_fact/R_fact/L_fact/S_fact
if L > 0:
total -= GetInvalidLeftMoveGames(t,n,R,L,S)
if R > n-1:
total -= GetInvalidRightMoveGames(t,n,R,L,S)
return total
My second attempt at this. However, the stack overflows for large game sizes (1000,1000). If there were a generator for the permutations that didn't use O(N!) space then this could work out.