What is the largest value one can get in game 2048 without new tiles appear - algorithm

This is a simplified version of the famous game 2048. Given a 4x4 grids with some values chosen from {0, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048}. A value of 0 indicates that the position in the grid is unoccupied. What is the largest tile that can be produced by any (possible length-zero) sequence of moves (up, down, left,right) from the given game setup, if no new tiles were to be introduced to the grid?
For example, given
2 64 4 32
8 16 8 4
4 32 4 0
2 2 0 0
the answer is 128:
The problem can probably be solved by those AI algorithms (minmax for example), however, I guess that will definitely be an overkill. Is there any simpler algorithm to solve this?

The simplest algorithm you can do in this case is a graph-based search. Every node of the graph represents a current state i of the grid. Every node has 4 children, representing state i+1 and therefore each edge represents a move (up, down, left, right).
So assume you start from the given state, node 1. Then you have to apply the 4 moves and simulate them. That is, node 1 has 4 children, node 2(with left movement), node 3(with right movement) and so on. For each movement you have simulate what that movement does. Store in each node the current state and the value of the maximum tile.
The algorithm would finish whenever there are no more possible movements. So looking for the maximum value of all the leaf nodes would do it.
The pseudo-code would be something like:
input: current state s_current
output: max tile value M
-----
queue <- s_current
while !queue.empty do
s = queue.pop
for each m in move // move contains the 4 moves.
s' = simulate(s,m)
if s' != 0 //so the move was possible
queue.add(s')
else
mark s' as leaf node
M = max_tile(s')
if M > M_current update M
DISCLAIMER: I have not checked for errors in the pseucode, probably some minor steps are missing.
Note that depending on queue datastructure, you are actually implementing a Breath First Search or Depth First Searh, which are the simplest graph algorithms you can implement. Actually, the most difficult part I see here is the simulate() function, which is the one that actually implements the logic of your game. I guess there are much better algorithms, but this is the most simple thing (and actually is not that bad :) )

Related

Alpha-beta pruning with ties causes avoidable loss

In developing software to play some simple strategy games, I am using standard techniques for searching and evaluation, including alpha-beta pruning. But I have encountered an unexpected problem which results in one player choosing an ultimately losing move instead of one that ties the game.
Imagine this scenario: The MAX player has at least two moves to evaluate at depth D. One results in a tie, so its value is 0 and alpha is set to 0. In searching a second MAX move, with alpha = 0, at depth D+1, MIN has at least two replies to evaluate. One of them is a forced win for MIN. An earlier MIN reply results in a tie, so its value is 0, and beta is set to 0. This triggers an alpha-beta cutoff for MIN because beta 0 <= alpha 0. So the later forced win for MIN is never seen, and that second MAX move gets a value of 0. Thus it may be chosen instead of the first MAX move (a tie), resulting in an avoidable loss for MAX.
Here's a more concrete example: A tic-tac-toe board can be represented as…
0 1 2
3 4 5
6 7 8
Let's say that the MIN player chooses square 8. The MAX player has eight possible replies. The first four replies examined – squares 0, 1, 2, and 3 – are all evaluated to eventually result in a loss for MAX. Square 4 is evaluated as a tie, with a value of 0, and thus alpha is set to 0. Square 5 is next evaluated, and as part of that process, the alpha value of 0 is passed down, and the possible replies by MIN are evaluated one at a time. Square 0 is evaluated for MIN as a tie, and thus beta is set to 0. The next possible reply by MIN, square 1, is also a tie. At this point, both beta and alpha are 0, triggering an alpha-beta pruning of the remaining MIN replies. These include squares 4, 6, and 7, all of which allow MIN to win. But these are never seen, because of the alpha-beta pruning. A value of 0 is passed up the search tree. Consequently, MAX incorrectly sees square 5 as a tie just as good as square 4, even though playing at square 5 would likely result in a loss.
Does anyone see a problem with this analysis? Isn't this a problem with alpha-beta pruning?

Find Minimum Time to Occupy Grid

Problem:
Consider a patient suffering from skin infection and germs are spreading all over rapidly. Assume that skin surface is scaled as a rectangular grid of size MxN and cells are marked by 0 and 1 where 0 represents non affected region on skin and 1 represents affected region on skin. Germs can move from one cell of grid to another in 4 possible directions (right, left, up, down) but can move to only one cell at a time in one direction and affect that cell in 1 sec. Doctor currently who is treating the patient see's status and wants to know the time left for him to save him before the germs spread all over the skin and patient dies. Can you help to estimate the minimum time taken for the germs to completely occupy skin surface?
Input: : Current status of skin. (A matrix of size MxN with 1's and 0's which represents affected and non affected area)
Output: : Min time in sec to cover all over the grid.
Example:
Input:
[1 1 0 0 1]
[0 1 1 0 0]
[0 0 0 0 1]
[0 1 0 0 0]
Output: 2 seconds
Explanation:
After 1 sec from input, matrix could be as below
[1 1 1 0 1]
[1 1 1 0 1]
[0 1 1 0 1]
[0 1 1 0 1]
In next sec, matrix is completely filled by 1's
I will not present a detailed solution here, but some thoughts that hopefully may help you to write your own program.
First step is to determine the kind of algorithm to implement. The optimal way would be to find a simple and fast ad hoc solution for this problem. In the absence of such a solution, for this kind of problems, classical candidates are DFS, BFS, A* ...
As the goal is to find the shortest solution, it seems natural to consider BFS first, as once BFS finds a solution, we know that it is the shortest ones and we can stop the search. However, then, we have to consider avoiding inflation of the nodes, as it would lead not only to a huge calculation time, but also a huge memory.
First idea to avoid node inflation is to consider that some 1 cells can only be expended in one another cell. In the posted diagram, for example the cell (0, 0) (top left) can only be expended to cell (1, 0). Then, after this expansion, cell (1, 1) can only move to cell (2, 1). Therefore, we know it would be suboptimal to move cell (1,1) to cell (1,0). Therefore: move such cells first.
In a similar way, once an infected cell is surrounded by other infected cells only, it is no longer necessary to consider it for next moves.
At the end, it would be convenient to have a list of infected cells, together with the number of non-infected cells that each such cell can move to.
Another idea to limit the number of nodes is to detect duplicates, as it is likely here that many of them will exist. For that, we have to define a kind of hashing. The used hash function does not need to be 100% efficient, but need to be calculated rapidly, and if possible in a recursive manner. If we obtain B diagram from A diagram by adding a 1-cell at position (i, j), then I propose something like
H(B) = H(A)^f(i, j)
f(i, j) = a*(1024*i+j)%b
Here, I used the fact that N and M are less than 1000.
Each time a new diagram is consider, we have to calculate the corresponding H value and check if it exists already in the set of past diagrams.
I'm not sure how far I would get with this in an interview situation. After some thought, rather than considering solutions that store more than one full board state, I would rather consider a greedy priority queue since a strong heuristic for the next zero-cell candidates to fill seems to be:
(1) healthy cells that have the least neighbouring infected cells (but at least one, of course),
e.g., choose A over B
1 1 B 0 1
0 1 1 0 0
0 0 A 0 1
0 1 0 0 0
and (2) break ties by choosing first the healthy cells that when infected will block the least infected cells.
e.g., choose A over B
1 1 1 0 1
1 B 1 0 A
0 0 0 0 1
0 1 0 0 0
An interesting observation is that any healthy cell destination can technically be reached in time Manhattan-distance from the nearest infected cell, where the cell leading such a "crawl" continually chooses the single move that brings us closer to the destination. We know that at the same time, though, this same infected-cell "snake" produces new "crawlers" that could reach any equally far or closer neighbours. This makes me wonder if there may be a more efficient way to determine the lower-bound, based on counts of the farthest healthy cells.
This is a variant of the multi-agent pathfinding problem (MAPF). There is a ton of recent work on this topic, but earlier modern work is a good starting point for finding optimal solutions to this problem - for instance the operator decomposition approach.
To do this you would order the agents (germs) 1..k. Then, you would start a search where you generate all possible first moves for germ 1, followed by all possible first moves for germ 2, and so on, where moves for an agent are to stay in place, or to spread to an adjacent unoccupied location. With 4 possible actions for each germ, there are up to 4^k possible actions between complete states. (Partial states occur when you haven't yet assigned actions to all k agents.) The number of actions is exponential, meaning you may run up against resource constraints (time or space) fairly quickly. But, there are only 2^(MxN) states possible. (Since agents don't go away, it's actually 2^(MxN-i) where i is the number of initial germs.)
Every time all (k) germs have considered a possible action, you have a new complete state. (And k then increases for the next iteration.) The minimum time left comes from the shallowest complete state which has the grid filled. A bit of brute-force computation will find the shortest solution. (Quite a bit in the case of large grids.)
You could use a BFS to find the first state that is completely filled. But, A* might do much better. As a heuristic, you could consider that all adjacent locations of all cells were filled in each step, and then compute the number of steps required to fill the grid under that model. That gives a lower bound on the time required to fill the full grid.
But, there are many more optimizations. The reason to do operator decomposition is that you could order the moves to take the best moves first and not consider the weaker possibilities (eg all germs don't spread). You could also use a partial-expansion approach (EPEA*) to avoid generating a lot of clearly suboptimal policies for the germs.
If I was asking this as an interview questions I might be looking to see someone formulate the problem (what are actions, what are states), come up with the lower bound on the solution (every germ expands to every adjacent cell), come up with an algorithm, and perhaps analyze how hard the problem is, in order of increasing difficulty.

Minimum number of steps to sort 3x3 matrix in a specific way

So I started practicing some algorithms and programming before university starts and I ran into this problem:
Given a 3x3 matrix containing the numbers from 0 to 8, find the minimum number of steps required to sort the matrix in the following format:
1 2 3
4 5 6
7 8 0
In one move it is only allowed to pick a cell that is adjacent to the cell which contains the 0 and swap those two cells.
Now, I am really stuck with this one and have no idea how to begin. Any tips and ideas to get me started are appreciated.
This is not homework if anyone thinks that way, I am just trying to exercise and by moving to tougher problems I got stuck. I am not looking for anyone to write the code for me, I just need a point in the right direction because I really want to understand the algorithm behind this. Thank you.
Note: This is actually an AI problem, and not a trivial data structure/algorithm problem.
This problem is called the n-puzzle problem. The example in your question is the 8-puzzle problem.
The way to solve this problem is by trying to shuffle the boxes in a way that each step gets you closer to your final goal. Think of this as a Greedy approach (Best-first search). The best algorithm to use here is the A* algorithm.
We define a state of the game to be the board position, the number of
moves made to reach the board position, and the previous state. First,
insert the initial state (the initial board, 0 moves, and a null
previous state) into a priority queue. Then, delete from the priority
queue the state with the minimum priority, and insert onto the
priority queue all neighboring states (those that can be reached in
one move). Repeat this procedure until the state dequeued is the goal
state. The success of this approach hinges on the choice of priority
function for a state. We consider two priority functions:
Hamming priority function. The number of blocks in the wrong position, plus the number of moves made so far to get to the state. Intutively, a state with a small number of blocks in the wrong position is close to the goal state, and we prefer a state that have been reached using a small number of moves.
Manhattan priority function. The sum of the distances (sum of the vertical and horizontal distance) from the blocks to their goal positions, plus the number of moves made so far to get to the state.
For example, the Hamming and Manhattan priorities of the initial state
below are 5 and 10, respectively.
8 1 3 1 2 3 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8
4 2 4 5 6 ---------------------- ----------------------
7 6 5 7 8 1 1 0 0 1 1 0 1 1 2 0 0 2 2 0 3
initial goal Hamming = 5 + 0 Manhattan = 10 + 0
We make a key oberservation: to solve the puzzle from a given state
on the priority queue, the total number of moves we need to make
(including those already made) is at least its priority, using either
the Hamming or Manhattan priority function. (For Hamming priority,
this is true because each block that is out of place must move at
least once to reach its goal position. For Manhattan priority, this is
true because each block must move its Manhattan distance from its goal
position. Note that we do not count the blank tile when computing the
Hamming or Manhattan priorities.)
Consequently, as soon as we dequeue a state, we have not only
discovered a sequence of moves from the initial board to the board
associated with the state, but one that makes the fewest number of
moves.
(Source)

Touching segments

Can anyone please suggest me algorithm for this.
You are given starting and the ending points of N segments over the x-axis.
How many of these segments can be touched, even on their edges, by exactly two lines perpendicular to them?
Sample Input :
3
5
2 3
1 3
1 5
3 4
4 5
5
1 2
1 3
2 3
1 4
1 5
3
1 2
3 4
5 6
Sample Output :
Case 1: 5
Case 2: 5
Case 3: 2
Explanation :
Case 1: We will draw two lines (parallel to Y-axis) crossing X-axis at point 2 and 4. These two lines will touch all the five segments.
Case 2: We can touch all the points even with one line crossing X-axis at 2.
Case 3: It is not possible to touch more than two points in this case.
Constraints:
1 ≤ N ≤ 10^5
0 ≤ a < b ≤ 10^9
Let assume that we have a data structure that supports the following operations efficiently:
Add a segment.
Delete a segment.
Return the maximum number of segments that cover one point(that is, the "best" point).
If have such a structure, we can get use the initial problem efficiently in the following manner:
Let's create an array of events(one event for the start of each segment and one for the end) and sort by the x-coordinate.
Add all segments to the magical data structure.
Iterate over all events and do the following: when a segment start, add one to the number of currently covered segments and remove it from that data structure. When a segment ends, subtract one from the number of currently covered segment and add this segment to the magical data structure. After each event, update the answer with the value of the number of currently covered segments(it shows how many segments are covered by the point which corresponds to the current event) plus the maximum returned by the data structure described above(it shows how we can choose another point in the best possible way).
If this data structure can perform all given operations in O(log n), then we have an O(n log n) solution(we sort the events and make one pass over the sorted array making a constant number of queries to this data structure for each event).
So how can we implement this data structure? Well, a segment tree works fine here. Adding a segment is adding one to a specific range. Removing a segment is subtracting one from all elements in a specific range. Get ting the maximum is just a standard maximum operation on a segment tree. So we need a segment tree that supports two operations: add a constant to a range and get maximum for the entire tree. It can be done in O(log n) time per query.
One more note: a standard segment tree requires coordinates to be small. We may assume that they never exceed 2 * n(if it is not the case, we can compress them).
An O(N*max(logN, M)) solution, where M is the medium segment size, implemented in Common Lisp: touching-segments.lisp.
The idea is to first calculate from left to right at every interesting point the number of segments that would be touched by a line there (open-left-to-right on the lisp code). Cost: O(NlogN)
Then, from right to left it calculates, again at every interesting point P, the best location for a line considering segments fully to the right of P (open-right-to-left on the lisp code). Cost O(N*max(logN, M))
Then it is just a matter of looking for the point where the sum of both values tops. Cost O(N).
The code is barely tested and may contain bugs. Also, I have not bothered to handle edge cases as when the number of segments is zero.
The problem can be solved in O(Nlog(N)) time per test case.
Observe that there is an optimal placement of two vertical lines each of which go through some segment endpoints
Compress segments' coordinates. More info at What is coordinate compression?
Build a sorted set of segment endpoints X
Sort segments [a_i,b_i] by a_i
Let Q be a priority queue which stores right endpoints of segments processed so far
Let T be a max interval tree built over x-coordinates. Some useful reading atWhat are some sources (books, etc.) from where I can learn about Interval, Segment, Range trees?
For each segment make [a_i,b_i]-range increment-by-1 query to T. It allows to find maximum number of segments covering some x in [a,b]
Iterate over elements x of X. For each x process segments (not already processed) with x >= a_i. The processing includes pushing b_i to Q and making [a_i,b_i]-range increment-by-(-1) query to T. After removing from Q all elements < x, A= Q.size is equal to number of segments covering x. B = T.rmq(x + 1, M) returns maximum number of segments that do not cover x and cover some fixed y > x. A + B is a candidate for an answer.
Source:
http://www.quora.com/What-are-the-intended-solutions-for-the-Touching-segments-and-the-Smallest-String-and-Regex-problems-from-the-Cisco-Software-Challenge-held-on-Hackerrank

Intresting puzzle

Task definition:
I have a matrix of natural numbers. The task is to find path from the top-left corner of matrix to bottom-right corner of matrix and dial maximum score.
Rules of navigation: if you are located in [i][j] you can move:
a) to [i][j-1], [i][j+1], [i+1][j] cells and dial zero points
b) to [i+1][j+1] and dial matrix[i][j] points
Little example:
Assume you have score 50and matrix
0 3 5 3 2
4 7 2 5 2
4 3 5 2 5
Assume you are in [1][1] cell (matrix[1][1] = 7). You can navigate to:
a) [1][0] cell with 50 score
b) [1][2] cell with 50 score
c) [2][1] cell with 50 score
d) [2][2] cell with 57 score
What a problem:
I solve this task in very slow way...
I try to implement in with help of recursion. It's easy if you just want to find maximum score. Something like
public int loop(int i, int j) {
int left = loop(i, j-1);
int top = loop(i-1, j);
int diagonal = loop(i-1,j-1) + matrix[i-1][j-1];
return maximum(left, top, diagonal);
}
BUT, I want to find a path with maximum score! And it's very time/memory consuming.
Why it's time/memory consuming:
And there is one problem: I need store path-collection and pass it as a parameter to the loop method. But loop method forks on each iteration and I have to copy path-collection thee times an iteration. Otherwise, each of loop forks will modify common path-collection and finally I will have in it all possible paths. I mean if between left, top & diagonal the biggest is left that we must not to include paths linked with top and diagonal.
Question:
How to solve it in right way?
EDIT:
Actually there is no need to find the full path. It only need to find point's in which you dial a score (in which you make a diagonal moves)
You don't need dynamic programming nor brute force for this!
To see why, let's analyze the rules:
You can move in direction j freely (left & right), so there's no reason to be careful about that direction - you can move into the optimial horizontal postion whenever you want.
Once you increase i (down) there's no way back (though you can increase i without gaining points). Each increase of i should net the maximal amount of points.
You gain points by leaving a cell, but you can only ever leave a row once.
That means you can subdivide this problem and do not need dynamic programming: you can move to the optimal j location, then take one diagonal step; repeat until done.
The optimal i step is moving from a non-last cell in a row with the highest value. You can't move from the last cell because there's no diagonal move possible - so if your matrix has only one column (or row for that matter) you'll never gain points. You can't lose points because the values are natural numbers (but if negative numbers were allowed, you can still skip a row).
In more detail, the optimal path is then found by...
Does the matrix have just one column or row? Move right repeatedly without gaining points then end the program. You can't do much here.
find the maximum value in the current row, ignoring the last value.
generate 'j' moves towards a maximally-valued cell, then move diagonally.
If you're not on the last row, go back to step 2.
You're on the last row and cannot gain more points; just generate moves towards the bottom-right corner to finish your path.
That's it!
Note that there may be multiple maximal paths, your problem specification doesn't guarantee a unique solution.
EDIT: If you don't need the actual path, but just the numbers you scored, the algorithm is much easier - remove or disregard the last row and last column, then for each i (row) return the maximum value in that row.
EDIT:
I misread the question to being just moving down and to the right (ie: j could only change to j or j+1.) so this answer is wrong.
You can use dynamic programming to solve this problem. Greedy doesn't exactly work because you can only travel "down and to the right".
The naive dynamic programming solution would essentially "work backwards" in a literal sense and start from the bottom-right and compute max score when starting at that cell.
Starting from the right-left, and from bottom-up, you can compute the best score you can get from that score simply. You do this for the m x n matrix, then you start from the top left and choose the direction that has the max score.

Resources