Misère Nim

Sort by

recency

|

27 Discussions

|

  • + 0 comments

    If you're familiar with Nim-Sum, this problem will be very easy; otherwise, it's incredibly difficult. This type of problem should be used to test well-read individuals, not programmers.

        let allOnes = true;
        
        for (let stone of stones) {
            xorSum ^= stone;
            if (stone > 1) {
                allOnes = false;
            }
        }
        
        if (allOnes) {
            return stones.length % 2 === 0 ? "First" : "Second";
        } else {
            return xorSum === 0 ? "Second" : "First";
        }
    
  • + 0 comments

    def misereNim(s):

    xor = reduce((lambda x, y: x ^ y), s)
    if max(s) == 1:
        return "First" if xor == 0 else "Second"
    else:
        return "Second" if xor == 0 else "First"
    
  • + 0 comments

    Here is my solution: 1) For 1 or 2 piles, it is easy to figure out the solution. 2) Win only if the opponent can not figure out how to win for all possible moves. 3) A depth-first seach for each combinition. 4) Minor optimizations.

    import static org.junit.Assert.assertEquals;

    import java.util.LinkedList; import java.util.List;

    import org.junit.Test;

    public class MisereNim {

    private static boolean firstMove = true;
    private static Boolean res = null;
    
    public static String misereNim(List<Integer> s) {
        firstMove = true;
        LinkedList<Integer> sList = new LinkedList<>(s);
        for (int i = 0; i < s.size(); ++i) {
            if (sList.get(i) <= 0) {
                sList.remove(i);
            }
        }
        sList.sort((Integer i, Integer j)-> (i < j)? +1 : ((i == j)? 0 : -1));
        move(sList, true);
        if (!res)
            firstMove = !firstMove;
        return firstMove? "First" : "Second";       
    }
    
    private static void win(final LinkedList<Integer> sList) {
        /*
        For one or two piles, find the optimal solution
         */
        switch (sList.size()) {
            case 1:
                res = sList.get(0) > 1;
                break;
            case 2:
                if ((sList.get(0) == 1) || (sList.get(1) == 1))
                    res = true;
                else if (sList.get(0) == 2) 
                    res = sList.get(1) != 2;
                else if (sList.get(1) == 2) 
                    res = sList.get(0) != 2;
                else
                    res = ((sList.get(0) - 2 + sList.get(1) - 2) % 2) == 1;
                break;
            default:
                res = null;
                break;
        }
    }
    
    private static void move(final LinkedList<Integer> sList, boolean first) {
        /*
        win only if the opponent can not figure out how to win for all possible moves.
        depth-first search, for optimal move.
    
        The constraint of 1 <= s[i] <= 10^9 means that the approach has to be optimized: to reduce either as 1, 2 or as 3.
        If there is even number of piles each with 1 stone, the first will win.
        If there is one pile with more than 1 stone, the first will win.
    
        timed out
        */
        firstMove = first;
        win(sList);
        if (res != null)
            return;
        int prev = 0;
        for (int i = 0; i < sList.size(); ++i) {
            if (prev == sList.get(i))
                continue;
            prev = sList.get(i);
            LinkedList<Integer> sSub = new LinkedList<>(sList);
            sSub.remove(i);
            firstMove = !first;
            win(sSub);
            if ((res != null) && (res == false))
                return;
            if (res == null) {
                move(sSub, !first);
            }
            if ((res == false) && (first != firstMove)) {
                return;
            } 
            for (int j = 1; (j < sList.get(i)) && (j <= 3); j++) {
                sSub = new LinkedList<>(sList);
                sSub.set(i, j);
                firstMove = !first;
                win(sSub);
                if (res == null) {
                    move(sSub, !first);
                }
                if ((res == false) && (first != firstMove)) {
                    return;
                } 
            }
        }
        firstMove = first;
        res = false;
        return;
    

    }

    @Test
    public void testOne_direct() {
        assertEquals("Second", misereNim(List.of(1)));
        assertEquals("First",  misereNim(List.of(1,1)));      // ->[1]
    }
    
    @Test
    public void testOne() {
        assertEquals("Second", misereNim(List.of(1,1,1))); // ->[1,1]
    }
    
    @Test
    public void testTwo_direct() {
        assertEquals("First",  misereNim(List.of(2)));             // ->[1]
        assertEquals("First",  misereNim(List.of(1,2)));        // ->[1]
        assertEquals("Second", misereNim(List.of(2,2)));        // ->[1,2], [2]
    }
    
    @Test
    public void testTwo() {
        assertEquals("First",  misereNim(List.of(1,1,2)));   // ->[1,1,1]
        assertEquals("First",  misereNim(List.of(1,2,2)));   // ->[2,2]
        assertEquals("First",  misereNim(List.of(2,2,2)));   // ->[2,2]
    }
    
    @Test
    public void testThree_direct() {
        assertEquals("First",  misereNim(List.of(3)));             // ->[1]
        assertEquals("First",  misereNim(List.of(1,3)));        // ->[1]
        assertEquals("First",  misereNim(List.of(2,3)));        // ->[2,2]       
        assertEquals("Second", misereNim(List.of(3,3)));        // ->[2,3], [1,3], [3]
    }
    
    @Test
    public void testThree() {
        assertEquals("First",  misereNim(List.of(1,1,3)));   // ->[1,1,1]
        assertEquals("Second", misereNim(List.of(1,2,3)));   // ->[2,3], [1,1,3],[1,3],[1,2],[1,2,1],[1,2,2]
        assertEquals("First", misereNim(List.of(1,3,3)));   // ->[3,3]
        assertEquals("First", misereNim(List.of(2,2,3)));   // ->[1,2,3]
        assertEquals("First", misereNim(List.of(2,3,3)));   // ->[3,3]
        assertEquals("First", misereNim(List.of(3,3,3)));   // ->[3,3]
    }
    
    @Test
    public void testSample() {
        assertEquals("First",  misereNim(List.of(9,8,4,4,7)));
    }
    

    }

  • + 0 comments

    Win or loose depends on number of remaining of pile of "1". So, if there are pile of ">1" value, player can use those piles to controller number of remaining pile of "1". Both player can use above pile to controller number of remaining of pile of "1", "Second" player always able to override controll of "Frist" player if number of pile of ">1" value is even && larger than 2.

  • + 0 comments

    I believe the testcases's result is incorrect.

    My solution:

    public static string misereNim(List<int> s)
    {
        var pileOfOneCount = s.Count(x => x == 1);
        var pileOfOtherCount = s.Count(x => x > 1);
    
        if (pileOfOtherCount == 0)
        {
            return pileOfOneCount % 2 == 0 ? "First" : "Second";
        }
    
        return pileOfOtherCount % 2 == 0 ? "Second" : "First";
    }