from games_puzzles_algorithms.puzzles.sliding_tile_puzzle import SlidingTilePuzzle
from games_puzzles_algorithms.puzzles.solvable_sliding_tile_puzzle import SolvableSlidingTilePuzzle
import unittest
import random

class TestSlidingTilePuzzle(unittest.TestCase):
    
    def test_init(self):
        size = 3
        puzzle = SlidingTilePuzzle(size)
        self.assertEqual(puzzle.size1, size)
        self.assertEqual(puzzle.size2, size)
        self.assertEqual(puzzle.puzzle.size(), (size, size))
        self.assertEqual(puzzle.puzzle[puzzle.blank_index], 0)
        self.assertLessEqual(puzzle.num_correct_tiles, size * size)
        self.assertGreaterEqual(puzzle.num_correct_tiles, 0)

    def test_seed_init(self):
        size = 3
        seed = 1
        a = SlidingTilePuzzle(size, seed)
        b = SlidingTilePuzzle(size, seed)
        self.assertEqual(a.size1, b.size1)
        self.assertEqual(a.size2, b.size2)
        for i in range(size):
            for j in range(size):
                self.assertEqual(a.puzzle[(i, j)], b.puzzle[(i, j)])
        self.assertEqual(a.blank_index, b.blank_index)
        self.assertEqual(a.num_correct_tiles, b.num_correct_tiles)
        
    def test_init_rectangle(self):
        size1 = 2
        size2 = 3
        puzzle = SlidingTilePuzzle(size1=size1, size2=size2)
        self.assertEqual(puzzle.size1, size1)
        self.assertEqual(puzzle.size2, size2)
        self.assertEqual(puzzle.puzzle.size(), (size1, size2))
        self.assertEqual(puzzle.puzzle[puzzle.blank_index], 0)
        self.assertLessEqual(puzzle.num_correct_tiles, size1 * size2)
        self.assertGreaterEqual(puzzle.num_correct_tiles, 0)        

    def test_is_solved_false(self):
        size = 2
        seed = 10
        puzzle = SlidingTilePuzzle(size, seed)
        self.assertFalse(puzzle.is_solved())

    def test_is_solved_true(self):
        size = 2
        seed = 10
        puzzle = SlidingTilePuzzle(size, seed)
        puzzle.apply_move(SlidingTilePuzzle.DIRECTIONS["down"])
        puzzle.apply_move(SlidingTilePuzzle.DIRECTIONS["right"])
        puzzle.apply_move(SlidingTilePuzzle.DIRECTIONS["up"])
        puzzle.apply_move(SlidingTilePuzzle.DIRECTIONS["left"])
        puzzle.apply_move(SlidingTilePuzzle.DIRECTIONS["down"])
        puzzle.apply_move(SlidingTilePuzzle.DIRECTIONS["right"])
        self.assertTrue(puzzle.is_solved())

    def test_correct_num(self):
        size = 3
        puzzle = SlidingTilePuzzle(size)
        number = 0
        for i in range(size):
            for j in range(size):
                self.assertEqual(puzzle.correct_num((i, j)), number)
                number += 1
                
    def test_correct_num_rectangle(self):
        size1 = 3
        size2 = 2
        puzzle = SlidingTilePuzzle(size1=size1, size2=size2)
        number = 0
        for i in range(size1):
            for j in range(size2):
                self.assertEqual(puzzle.correct_num((i, j)), number)
                number += 1

    def test_correct_tile(self):
        size = 3
        puzzle = SlidingTilePuzzle(size)
        number = 0
        for i in range(size):
            for j in range(size):
                self.assertEqual(puzzle.correct_tile(number), (i, j))
                number += 1
                
    def test_correct_tile_rectangle(self):
        size1 = 2
        size2 = 3
        puzzle = SlidingTilePuzzle(size1=size1, size2=size2)
        number = 0
        for i in range(size1):
            for j in range(size2):
                self.assertEqual(puzzle.correct_tile(number), (i, j))
                number += 1

    def test_apply_move_up(self):
        size = 2
        seed = 5
        puzzle = SlidingTilePuzzle(size, seed)
        puzzle.apply_move(SlidingTilePuzzle.DIRECTIONS['up'])
        self.assertEqual(puzzle.puzzle[(0, 0)], 3)
        self.assertEqual(puzzle.puzzle[(0, 1)], 1)
        self.assertEqual(puzzle.puzzle[(1, 0)], 0)
        self.assertEqual(puzzle.puzzle[(1, 1)], 2)
        self.assertEqual(puzzle.puzzle[puzzle.blank_index], 0)        

    def test_apply_move_down(self):
        size = 2
        seed = 10
        puzzle = SlidingTilePuzzle(size, seed)
        puzzle.apply_move(SlidingTilePuzzle.DIRECTIONS['down'])
        self.assertEqual(puzzle.puzzle[(0, 0)], 3)
        self.assertEqual(puzzle.puzzle[(0, 1)], 0)
        self.assertEqual(puzzle.puzzle[(1, 0)], 1)
        self.assertEqual(puzzle.puzzle[(1, 1)], 2)
        self.assertEqual(puzzle.puzzle[puzzle.blank_index], 0)

    def test_apply_move_left(self):
        size = 2
        seed = 5
        puzzle = SlidingTilePuzzle(size, seed)
        puzzle.apply_move(SlidingTilePuzzle.DIRECTIONS['left'])
        self.assertEqual(puzzle.puzzle[(0, 0)], 1)
        self.assertEqual(puzzle.puzzle[(0, 1)], 0)
        self.assertEqual(puzzle.puzzle[(1, 0)], 3)
        self.assertEqual(puzzle.puzzle[(1, 1)], 2)
        self.assertEqual(puzzle.puzzle[puzzle.blank_index], 0)        

    def test_apply_move_right(self):
        size = 2
        seed = 10
        puzzle = SlidingTilePuzzle(size, seed)
        puzzle.apply_move(SlidingTilePuzzle.DIRECTIONS['right'])
        self.assertEqual(puzzle.puzzle[(0, 0)], 3)
        self.assertEqual(puzzle.puzzle[(0, 1)], 2)
        self.assertEqual(puzzle.puzzle[(1, 0)], 0)
        self.assertEqual(puzzle.puzzle[(1, 1)], 1)
        self.assertEqual(puzzle.puzzle[puzzle.blank_index], 0)

    def test_valid_moves_some(self):
        size = 2
        seed = 1
        puzzle = SlidingTilePuzzle(size, seed)
        moves = puzzle.valid_moves()
        moves.sort()
        expected_moves = [SlidingTilePuzzle.DIRECTIONS['up'],
                          SlidingTilePuzzle.DIRECTIONS['right']]
        expected_moves.sort()
        self.assertEqual(moves, expected_moves)

    def test_valid_moves_all(self):
        size = 4