function solveHand(players, table)
{
    var n = players.length;
    var entire = []; // array of 7 cards for each player
    var player = null; // temporary entry of entire[i]
	
    // temporarys
    var temp = [];
    
    // loopers
    var i=0;
    var j=0;
    var k=0;
    
    // other temporary integers
    var count = 0;
    var h = 0;
    var p = 0;
    var q = 0;
    var r = 0;
    
    var special = []; // high-card or undefined (used for straights and straight flushes)
    var finalists = []; // index of players fighting for kickers
    var flushSuit = -1;
    
    var win = []; // true for 'win or tie' or undefined
    var tie = false;
    
    for (i=0; i<n; i++)
    {
        entire[i] = players[i].concat(table);
    }
    
    var winType = 8; // default for high card
    
    /***
        Look for STRAIGHT FLUSH
        Cache the results of the flushes
    ***/

    for (i=0; i<n; i++)
    {
        temp = [0,0,0,0]; // suits
        player = entire[i];
        
        for (j=0; j<7; j++)
        {
            
            temp[player[j][1]]++; // fill the suits
        }
        
        for (j=0; j<4; j++) 
        {
            if (temp[j]<5) // no found flush
            {
                continue;
            }
            
            temp.length = 0; // array of true/false indicating flush faces
            for (k=0; k<7; k++)
            {
                if (player[k][1]==j)
                {
                    temp[player[k][0]] = true;
                }
            }
            
            // cache the flush in case it's needed later
            finalists[finalists.length] = i;
            flushSuit = j;
            
            // count from the top to find the highest straight flush
            count = 1; // count top card in advance
            for (k=temp.length-2; k>=-1; k--) // go to -1 for possible A2345
            {
                if (temp[(k+13)%13]) // allow count of ace twice
                {
                    count++;
                    if (count==5) // straight-flush found
                    {
                        special[i] = k+4; // store highest card
                        break;
                    }
                }
                else
                {
                    count=0;
                }
            }
            break; // player can only have 1 flush suit
        }
    }
    
    if (special.length) // somebody won with straight flush
    {
	j = 3; // start with lowest possible straight flush
	for (i=0; i<special.length; i++)
	{
	    if (special[i]>=j)
	    {
		if (special[i]>j)
		{
		    j = special[i];
		    win.length = 0; // other players lose
		}
		win[win.length] = i;
	    }
	}
	if (win.length > 1) // tie
	{
	    return null;
	}
	else // outright win
	{
            return [win[0], 0];
	}
    }
    
    /***
        Build card map
    ***/

    var cardMap = [];
    for (i=0; i<n; i++)
    {
        cardMap[i]=[0,0,0,0,0,0,0,0,0,0,0,0,0];
        player = entire[i];
        for (j=0; j<7; j++)
        {
            cardMap[i][player[j][0]]++;
        }
        cardMap[i] = cardMap[i].join('');
    }

    /**
        Look for 4 OF A KIND
    **/

    j = 0; // best player's quad type+1
    for (i=0; i<n; i++)
    {
        k = cardMap[i].indexOf('4') + 1;
        if (k)
        {
            if (j == k)
            {
                // if two players have the same quad then everybody does
                // look for high card
                j = 0; // best player's high card+1
                for (i=0; i<n; i++)
                {
                    k = cardMap[i].search(/[^04][04]*$/)+1; // highest card not in quad
                    if (k >= j)
                    {
                        tie = (k ==j );
                        
                        if (!tie)
                        {
                            j = k;
                            win.length = 0;
                        }
                        win[i] = true;
                    }
                }
                if (tie)
                {
                    return null;
                }
                else
                {
                    break; // one outright winner below
                }
            }
            if (k > j)
            {
                j = k;
                win.length = 0;
                win[i] = true;
            }
        }
    }
    if (j)
    {
        for (i=0; i<win.length; i++)
        {
            if (win[i])
            {
                return [i, 1];
            }
        }
    }

    /**
        Look for FULL HOUSE
    **/

    var j = -1; // best FH trip
    var k = -1; // best FH pair
    for (i=0; i<n; i++)
    {
        p = cardMap[i].lastIndexOf('3');
        if (p>=0 && p >= j)
        {
            if (p == (q = cardMap[i].indexOf('3')))
            {
                // pair could another 3 or a 2 of kind
                q = cardMap[i].lastIndexOf('2');
            }
            if (q>=0 && (p > j || q >=k)) // equal or better full house
            {
                tie = !(p > j || q > k);
                if (!tie) // is a better full house
                {
                    j = p;
                    k = q;
                    win.length = 0;
                }
                win[i] = true;
            }
            
        }
    }
    if (j >= 0) // record the results
    {
        if (tie)
        {
            return null;
        }
        
        for (i=0; i<players.length; i++)
        {
            if (win[i])
            {
                return [i, 2];
            }
        }
        
    }

    /**
        Look for FLUSH
    **/

    if (flushSuit >= 0) // we have some flushes!
    {
        // remove non-suited cards since they can't kick
        for (i=0; i<finalists.length; i++)
        {
            temp = cardMap[finalists[i]].split('');
            
            for (j=0; j<table.length; j++)
            {
                if (table[j][1]!=flushSuit)
                {
                    temp[table[j][0]]--;
                }
            }
            player = players[finalists[i]];
            
            for (j=0; j<2; j++)
            {
                if (player[j][1] != flushSuit)
                {
                    temp[player[j][0]]--;
                }
            }
            
            cardMap[finalists[i]] = temp.join('');
        }
        count = 5; // kickers
        p = 0; // sentinel value for flush
	winType = 3;
    }
    else
    {

        /**
            Look for STRAIGHT
        **/
        
        for (i=0; i<n; i++)
        {
            temp.length = 0; // temp holds faces
            for (j=0; j<7; j++)
            {
                temp[entire[i][j][0]] = true
            }
            // count from the top to find the highest straight
            count = 1; // count top card in advance
            for (k=temp.length-2; k>=-1; k--) // go to -1 for possible A2345
            {
                if (temp[(k+13)%13]) // allow count of ace twice
                {
                    count++;
                    if (count==5) // straight found
                    {
                        
                        special[i] = k+4; // store highest card
                        break;
                    }
                }
                else
                {
                    count=0;
                }
            }
        }
        
        if (special.length) // somebody won with straight
        {
            j = 3; // start with lowest possible straight
            for (i=0; i<special.length; i++)
            {
                if (special[i]>=j)
                {
                    if (special[i]>j)
                    {
                        j = special[i];
                        win.length = 0; // other players lose
                    }
                    win[win.length] = i; // win or tie
                }
            }
            if (win.length > 1) // tie
            {
                return null;
            }
            else // outright win
            {
                return [win[0], 4];
            }
        }
 
        /**
            Look for THREE OF A KIND
        **/
        
        temp.length = 0; // player's highest pair
        k = -1; // best pair any player has
        for (i=n-1; i>=0; i--)
        {
            j = temp[i] = cardMap[i].lastIndexOf('3');
            if (j > k)
            {
                k = j;
            }
        }
        
        // players who have three of kind go to finalists
        if (k > -1)
        {
            for (i=temp.length; i>=0; i--)
            {
                if (temp[i]==k)
                {
                    finalists[finalists.length] = i;
                }
            }
            
            count = 2; // kickers
            p = 1; // sentinel
	    winType = 5;
        }
        else
        {
            /**
                Look for 2 PAIR
            **/
            
            j = -1; // best pair
            k = -1; // 2nd best pair
            h = -1; // best kicker
            for (i=0; i<n; i++)
            {
                p = cardMap[i].lastIndexOf('2');
                if (p > -1 && p >= j)
                {
                    q = ('xx'+cardMap[i]).lastIndexOf('2',(p-1)+2)-2; // have to add the 'xx' to avoid in valid 2nd parameter
                    
                    if (q > -1 && (p > j || q >= k))
                    {
                        r = Math.max( // kicker
                                cardMap[i].lastIndexOf('1'),
                                ('xx'+cardMap[i]).lastIndexOf('2', (q-1)+2)-2 // could be "3 pair"
                            );
                            
                        if (p > j || q > k || r >= h)
                        {
                            if (p > j || q>k || r > h)
                            {
                                j = p;
                                k = q;
                                h = r;
                                win.length = 0;
                            }
                            win[win.length] = i;
                        }
                    }
                }
            }
            if (win.length)
            {
                if (win.length > 1) // tie
                {
                    return null;
                }
                else // outright win
                {
                    return [win[0], 6];
                }
            }
            
            /**
                Look for PAIR
            **/
            
            temp.length = 0; // player's highest pair
            k = -1; // best pair any player has
            for (i=n-1; i>=0; i--)
            {
                j = temp[i] = cardMap[i].lastIndexOf('2');
                if (j > k)
                {
                    k = j;
                }
            }
            
            // players who have top pair (top pair may be no pair) go to finalists
            for (i=temp.length; i>=0; i--)
            {
                if (temp[i]==k)
                {
                    finalists[finalists.length] = i;
                }
            }
            count = k < 0 ? 5 : 3; // kickers
            p = k < 0 ? 3 : 2; // sentinel value for flush
	    winType = k < 0 ? 8 : 7;
        }
    }

    /**
        Look for HIGH CARD
        
        Used for flushes, trips, 1 pairs and nothings
        By here finalists must be an array of the index
        of each player still in the running.
    **/
    
    for (i=finalists.length-1; i>=0; i--)
    {
	// IE can't lookup string indexes with [] notation
	cardMap[finalists[i]] = cardMap[finalists[i]].split('');
    }
    
     // check each face and who has it
    for (j=12; count>0 && finalists.length>1; j--) // count always reaches 0 before j does
    {
        for (i=finalists.length-1; i>=0; i--)
        {
            if (cardMap[finalists[i]][j]=='1') // player has a kicker
            {
                finalists.length = i+1; // players after in array didn't have this kicker, remove them
                count--;
                i--;
                for (; i>=0; i--)
                {
                    // now remove the plays before in the array that don't have this kicker
                    if (cardMap[finalists[i]][j]!='1')
                    {
                        finalists[i] = finalists[finalists.length-1];
                        finalists.length--;
                    }
                }
            }
        }
    }

    if (finalists.length > 1) // tie
    {
        return null;
    }
    else
    {
        return [finalists[0], winType];
    }
}
