require 'libscrab.rb'
require 'move.rb'
require 'rbtree'

class Scrabble
    attr_reader :Dict
    attr_accessor :Mx
    def initialize(file)
        Move.Mx = @Mx = Array.new(15);
        @Mx.map!{Array.new(15)};
        @Mx.each{|row|row.map!{''}};
        @Dict = Marshal.load(File.open(file).read).freeze;
    end


    def loadB(file)
        Move.Mx = @Mx = Array.new();
        File.open(file, "r").read.each { |row|
            @Mx.push(row.split("-").map{|x|x.gsub(/\s+/,'')});
        }
    end
    def saveB(file)
        File.open(file, "w+") { |f|
            @Mx.each {|row| f.puts row.map{|x|
                x.empty? ? " " : x}.join("-");}
        }
    end


    def search(letters)
        @results = Array.new();
        horizA, vertA = anchors();
        horizA.each { |coord|
            @srow, @scol = coord[0].freeze, coord[1].freeze;
            SLeft(letters);
        }
        vertA.each { |coord|
            @srow, @scol = coord[0].freeze, coord[1].freeze;
            SUp(letters);
        }
        return @results.sort{|a,b| b <=> a};
    end


    def checkUp(origRow, col, lr)
        word, row = lr, origRow - 1;
        while(row >= 0 and not @Mx[row][col].empty?)
            word += @Mx[row][col];
            row -= 1;
        end
        if (origRow < 14)
            row = origRow + 1;
            if (not @Mx[row][col].empty?)
                word += "0" + @Mx[row][col];
            end
            row += 1;
            while(row <= 14 and not @Mx[row][col].empty?)
                word += @Mx[row][col];
                row += 1;
            end
        end
        nextL = nextSet(word);
        return (nextL.empty? ? false : nextL.first.empty?);
    end
    def checkLeft(row, origCol, lr)
        word, col = lr, origCol-1;
        while(col >= 0 and not @Mx[row][col].empty?)
            word += @Mx[row][col];
            col -= 1;
        end
        if (origCol < 14)
            col = origCol + 1;
            if (not @Mx[row][col].empty?)
                word += "0" + @Mx[row][col];
            end
            col += 1;
            while(col <= 14 and not @Mx[row][col].empty?)
                word += @Mx[row][col];
                col += 1;
            end
        end
        nextL = nextSet(word);
        return (nextL.empty? ? false : nextL.first.empty?);
    end


    def SLeft(lrs, row = @srow, col = @scol, word = "")
        start = word.empty?;
        while(col >= 0 and not @Mx[row][col].empty?)
            word += @Mx[row][col];
            col -= 1;
        end
        nextL = nextSet(word);
        return true if nextL.empty?;

        if (not start and nextL.first.empty?)
            @results.push(HMove.new(@srow, @scol, word));
        end

        if (nextL.first == '0' or nextL[1] == '0')
            SRight(lrs, @srow, @scol+1, word+'0');
        end
        return true if (col < 0);

        alone = ((row == 0 or @Mx[row-1][col].empty?) and
            (row == 14 or @Mx[row+1][col].empty?));

        (lrs & nextL).each { |lr|
            if (alone or checkUp(row, col, lr))
                SLeft(lrs.delOnce(lr), row, col-1, word+lr);
            end
        }
    end
    def SUp(lrs, row = @srow, col = @scol, word = "")
        start = word.empty?;
        while(row >= 0 and not @Mx[row][col].empty?)
            word += @Mx[row][col];
            row -= 1;
        end

        nextL = nextSet(word);
        return true if nextL.empty?;

        if (not start and nextL.first.empty?)
            @results.push(VMove.new(@srow, @scol, word));
        end

        if (nextL.first == '0' or nextL[1] == '0')
            SDown(lrs, @srow+1, @scol, word+'0');
        end
        return true if (row < 0);

        alone = ((col == 0 or @Mx[row][col-1].empty?) and
            (col == 14 or @Mx[row][col+1].empty?));

        (lrs & nextL).each { |lr|
            if (alone or checkLeft(row, col, lr))
                SUp(lrs.delOnce(lr), row-1, col, word+lr);
            end
        }
    end


    def SRight(lrs, row, col, word)
        while(col <= 14 and not @Mx[row][col].empty?)
            word += @Mx[row][col];
            col += 1;
        end

        nextL = nextSet(word);
        return true if nextL.empty?;

        if (nextL.first.empty?)
            @results.push(HMove.new(@srow, @scol, word));
        end
        return true if (col > 14);

        alone = ((row == 0 or @Mx[row-1][col].empty?) and
            (row == 14 or @Mx[row+1][col].empty?));

        (lrs & nextL).each { |lr|
            if (alone or checkUp(row, col, lr))
                SRight(lrs.delOnce(lr), row, col+1, word+lr);
            end
        }
    end
    def SDown(lrs, row, col, word)
        while(row <= 14 and not @Mx[row][col].empty?)
            word += @Mx[row][col];
            row += 1;
        end

        nextL = nextSet(word);
        return true if nextL.empty?;

        if (nextL.first.empty?)
            @results.push(VMove.new(@srow, @scol, word));
        end
        return true if (row > 14);

        alone = ((col == 0 or @Mx[row][col-1].empty?) and
            (col == 14 or @Mx[row][col+1].empty?));

        (lrs & nextL).each { |lr|
            if (alone or checkLeft(row, col, lr))
                SDown(lrs.delOnce(lr), row+1, col, word+lr);
            end
        }
    end


    def nextSet(req)
        @req = req;
        @reqL = @req.length;
        return nextHelper();
    end
    def nextHelper(lexDict = @Dict, char = 0)
        return [] unless lexDict;
        if (lexDict.kind_of? RBTree)
            return lexDict.keys if (char == @reqL);
            return nextHelper(lexDict[@req[char,1]], char+1);
        else
            words = lexDict.find_all{|word| (word =~ /^#{@req}/)};
            words.map!{|word| word[@reqL,1]}.uniq!;
            return words;
        end
    end


    def anchors()
        hzA, vtA = Array.new(), Array.new();
        if (not @Mx[14][14].empty?)
            hzA.push([14, 14]);
            vtA.push([14, 14]);
        end

        0.upto(13) { |r|
            if (not @Mx[14][r].empty?)
                hzA.push([14, r]) if (@Mx[14][r+1].empty?);
                vtA.push([14, r]);
            end

            if (not @Mx[r][14].empty?)
                vtA.push([r, 14]) if (@Mx[r+1][0].empty?);
                hzA.push([r, 14]);
            end

            0.upto(13) { |c|
                if (not @Mx[r][c].empty?)
                    hzA.push([r, c]) if (@Mx[r][c+1].empty?);
                    vtA.push([r, c]) if (@Mx[r+1][c].empty?);
                end
            }
        }
        return hzA, vtA;
    end


    def horIns(row, col, nword)
        word = nword.clone;
        while (not word.empty?)
            @Mx[row][col] = word.slice!(0,1);
            col += 1;
        end
    end
    def vertIns(row, col, nword)
        word = nword.clone;
        while (not word.empty?)
            @Mx[row][col] = word.slice!(0,1);
            row += 1;
        end
    end
end
