

/*****************************************************************************
 *
 *  Define the ComputerPlayer Object and Functions
 *
 *****************************************************************************/
function ComputerPlayer(aName, theSkillLevel)
{
    this.score = 0;
    this.hand = new Hand();
    this.row = null;
    this.skillLevel = theSkillLevel;

    this.top = 0;
    this.left = 0;
    this.position = 0;
    this.deck = 0;

    this.init(aName);

    this.startNewRound();

}

ComputerPlayer.prototype.isComputerPlayer = function()
{
    return true;
}

ComputerPlayer.prototype.activate = function()
{
    // indicate that this player is playing
    flyCard(new Card('castle'), this.left - 40, this.top+10, 'anchor', 2000, false);
}

ComputerPlayer.prototype.getHasRow = function()
{
    return this.row != null;
}

ComputerPlayer.prototype.getHand = function()
{
    return this.hand;
}

ComputerPlayer.prototype.init = function(aName)
{
    this.name = aName;
}

ComputerPlayer.prototype.calculateCurrentScore = function()
{
    var thand = this.hand.clone();
    return thand.getScore() + this.score;
}

ComputerPlayer.prototype.setAnchor = function(theLeft, theTop, theDeck, thePosition)
{
    this.top = theTop;
    this.left = theLeft;
    this.position = thePosition;
    this.deck = theDeck;

    // raise the deck
    var pos = getElementPosition('anchor');
    l = parseInt(pos.left) + this.left;
    t = parseInt(pos.top) + this.top;

    moveDiv('deck'+this.deck, l-5, t-50, null, true, 1);
    moveDiv('player'+this.position, l+160, t-50, null, true, 1000);

    this.updateLabel();
}


ComputerPlayer.prototype.updateLabel = function()
{
    var id = 'player' + this.position + 'Label';
    var label = document.getElementById(id);
    if (label != null)
    {
        label.innerHTML = '<table cellspacing="0" cellpadding="0"><tr><td nowrap><span style="color:yellow"><b>' + this.name +  ' (' +  this.score + ')</b>' +
                          '</span></td></tr></table>';
    }
}

/**
 *  Returns the name for this player
 */
ComputerPlayer.prototype.getName = function()
{
    return this.name;
}


ComputerPlayer.prototype.getScore = function()
{
    return this.score;
}




ComputerPlayer.prototype.startNewRound = function()
{
    // hide any cards the player had in his hand to begin with
    var hand = this.hand.toList();
    for (var i=0; i<hand.length; i++)
    {
        if ((hand[i].getColor() == 'wild') ||
            (hand[i].getColor() == 'plus2'))
        {
            hideDivById('r'+hand[i].getDivId());
        }
        hideDivById(hand[i].getDivId());
    }

    if (this.row != null)
    {
        hideDivById(this.row.getDivId());
    }

    this.hand = new Hand();
    this.row = null;
}

ComputerPlayer.prototype.startNewHand = function()
{
    this.row = null;
}


ComputerPlayer.prototype.addCard = function(card)
{
    if (card.getColor() != 'row')
    {
        this.hand.addCard(card);
    }

    if (card.getColor() == 'wild')
    {
        // place that card in the play area
        var cardTop = this.top - 35;
        var cardLeft = this.left + 40+ (10*this.hand.getWild().length);

        flyCard(card, cardLeft, cardTop,
                'anchor', 10 + this.hand.getWild().length, true);
    }
    else if (card.getColor() == 'plus2')
    {
        // place that card in the play area
        var cardTop = this.top - 35;
        var cardLeft = this.left + 135 + (10*this.hand.getPlus2().length);

        flyCard(card, cardLeft, cardTop,
                'anchor', 10 + this.hand.getPlus2().length, true);
    }
    else if (card.getColor() == 'row')
    {
        // place that card in the play area
        var cardTop = this.top - 45;
        var cardLeft = this.left;

        flyCard(card, cardLeft, cardTop, 'anchor', 50, false);

        this.row = card;
    }
    else
    {
        this.addCardToHand(this.hand.getColorIndex(card.getColor()), card, true);
    }
}

ComputerPlayer.prototype.addCardToHand = function(index, card, rotateAtDestination)
{
    var cardLeft = this.left + (37*index);
    var cardTop = this.top + (10*this.hand[card.getColor()].length);
    flyCard(card, cardLeft, cardTop,
            'anchor', 10 + this.hand[card.getColor()].length, rotateAtDestination);
}


ComputerPlayer.prototype.takeTurn = function(theRound)
{
    var rows = theRound.getRows();

    var rowsToTake = false;
    var freeRows = new Array();

    for (var i=0; i<rows.length; i++)
    {
        if ((rows[i] != null) && (rows[i].length > 1))
        {
            rowsToTake = true;
        }
        if ((rows[i] != null) && (rows[i].length < 4))
        {
            freeRows.push(i);
        }
    }

    // from hand determine what looks good
    var rowToTake = this.analyzeMove(rows, (freeRows.length == 0), theRound);

    // if analysis decided to take a row...then take it
    if (rowToTake >= 0)
    {
        //alert("Returned move to take is " + rowToTake);
        theRound._rowClick(rowToTake+1);
        return;
    }

    // must draw a card now
    assert(freeRows.length > 0, "Didn't like any rows but there's no free rows to draw a card");

    // flip up a card
    theRound._deckClick();

    // choose where to place it
    var row = this.analyzeRowPlacement(theRound, rows);
    theRound._rowClick(row+1);
}

ComputerPlayer.prototype.computeRoundScores = function()
{
    this.score += this.hand.getScore();
    this.updateLabel();
}

ComputerPlayer.prototype.areWeAlone = function(theRound)
{
    var players = theRound.getPlayers();
    for (var i=0;i<players.length;i++)
    {
        if ((players[i] != this) && !players[i].getHasRow())
        {
            return false;
        }
    }

    return true;
}

ComputerPlayer.prototype.analyzeMove = function(rows, mustTakeRow, theRound)
{
    // default action is to not take a row unless one looks good or we have to
    var rowToTake = -1;

    var bestWeight = -1000;
    var foundEmptyRow = false;
    var ties = new Array();

    var weAreAlone = this.areWeAlone(theRound);

    for (var i=0; i<rows.length; i++)
    {
        if ((rows[i] != null) && (rows[i].length > 1))
        {
            // find the weight of this row
            var weight = this.assignRowWeight(rows, i, theRound);

            // if it's the best so far
            if (weight > bestWeight)
            {
                // start a new bucket to hold ties at this weight
                bestWeight = weight;
                ties = new Array();
            }

            // if a tie then collect it in the bucket
            if (bestWeight == weight)
            {
                ties.push(i);
            }
        }
        if ((rows[i] != null) && (rows[i].length == 1))
        {
            foundEmptyRow = true;
        }
    }

    // cut our losses if all rows have cards and our current best score
    // loses us more than 2 points.
    if (!foundEmptyRow && (bestWeight < 0))
    {
        var pushLuck = Math.random()
        if (pushLuck > 0.25)
        {
            mustTakeRow = true;
            //alert("Cutting loses when no empty row  pushLuck=" + pushLuck +
            //    " bestWeight=" + bestWeight);
        }
        else
        {
            //alert("pushing luck when no empty row pushLuck=" + pushLuck);
        }
    }
    else if (weAreAlone && (ties.length > 0))
    {
        if (this.hand.getColorCountWithRow(rows[ties[0]]) > 4)
        {
            mustTakeRow = true;
        }
        else
        {
            //alert("not taking row when alone since won't make more than 4 colors");
        }
    }

    // if we have to take a row or the best one we found looks pretty good.
    if (mustTakeRow || (bestWeight >= 0))
    {
        // periodically push our luck if the cards we need are still
        // readily available (whatever that means)
        if (mustTakeRow || bestWeight > 2)
        {
            var myPick = Math.floor((Math.random() * 15791)) % ties.length;
            rowToTake = ties[myPick];
        }
    }

    //alert("rowToTake=" + rowToTake + " bestWeight=" + bestWeight);

    return rowToTake;
}

ComputerPlayer.prototype.assignRowWeight = function(rows, rowIndex, theRound)
{
    // if the row really stinks we return -1
    // if it's worth taking then we return an assigned weight
    // so that if multiple rows look good then the best one will be taken
    // (the largest weight)

    // if all colors in the row match my good colors then that's pretty good
    // but we might want to push our luck to get more cards in the draw
    var row = rows[rowIndex];
    if ((row == null) || (row.length <= 1))
    {
        return -1;
    }

    var players = theRound.getPlayers();
    var score = this.hand.scoreRow(row);
    var myDiff = (score.after - score.before);

    var colorCount = 0;
    var theirDiff = 0;
    for (var i=0; i<players.length; i++)
    {
        var player = players[i];
        // only look at players that have not taken a row
        if ((player != this) && !player.getHasRow())
        {
            var theirScore = player.getHand().scoreRow(row);
            theirDiff += (theirScore.after - theirScore.before);
        }
    }

    // The color in the row can look less appealing if others are
    // collecting those cards as well.
    if ((this.hand.getColorCount() < 3) &&
        (row.length == 2) &&
        (this.hand.getColorCount(row[1].getColor()) == 0) &&
        (row[1].getColor() != 'wild') &&
        (row[1].getColor() != 'plus2'))
    {
        // There is only one card in the row and I don't have
        // 3 colors yet.  I might want to collect it if it's
        // a color that not many players have. or I might want to
        // sour it if others have it
        var othersColorCount = theRound.getCollectorCount(row[1].getColor(), this);
        if (othersColorCount == 0)
        {
            myDiff += 2;
        }
        else
        {
            var takeMatch = (Math.random() > 0.75);

            if ((othersColorCount == 1) && takeMatch)
            {
                myDiff += 1;
            }
            else
            {
                myDiff = -1;
            }
        }
    }


    // if theirDiff ends up negative then the row is bad for other (in general)
    // and we don't want to sour our view of the row in this case so we never
    // let this go negative. In other words, we only want to influence us taking
    // the row if it appears to be looking good to our opponents.
    if (theirDiff < 0)
    {
        theirDiff = 0;
    }

    // Adjust for the 'guaranteed' points of these cards
    for (var i=1; i<row.length; i++)
    {
        if (row[i].getColor() == 'wild')
        {
            myDiff += 4;
        }
        if (row[i].getColor() == 'plus2')
        {
            myDiff += 2;
        }
    }

    var matchWeight = this.calculateMatchWeight(myDiff, theirDiff, theRound);

    return matchWeight;
}



ComputerPlayer.prototype.calculateMatchWeight = function(myDiff, theirDiff, theRound)
{
    var matchWeight = myDiff;
    if ((myDiff > 0) && (theirDiff > 0))
    {
        // If others want it (including me) then I like it more
        // so that they might not get it.
        matchWeight = myDiff + theirDiff;
        //alert("I (and others) like this card myDiff=" + myDiff + " theirDiff=" + theirDiff);
    }
    else if ((myDiff <= 0) && (theirDiff > 0))
    {
        // If others want it (not including me) then I'll take it
        // if others really want it and it's not painful for me to take it
        // (take one for the team so others don't benefit)
        if ((myDiff > -2) && (theirDiff > 10))
        {
            matchWeight = myDiff + theirDiff;
            //alert("I really don't like this card but others do myDiff=" + myDiff + " theirDiff=" + theirDiff);
        }
        else if (myDiff > -3)
        {
            matchWeight = 3 - this.hand.getColorCount();
            //alert("I kindof don't like this card but others do myDiff=" + myDiff + " theirDiff=" + theirDiff);
        }
    }

    return matchWeight;
}




ComputerPlayer.prototype.analyzeRowPlacement = function(theRound, theRows)
{
    var players = theRound.getPlayers();
    var theCard = theRound.getPendingCard();

    var rowScores = new Array();

    var theirWorstScore = 1000;
    var theirBestScore = -1000;

    for (var i=0; i<theRows.length; i++)
    {
        var myRowScore = 0;
        var theirRowScore = 0;

        if ((theRows[i] != null) && (theRows[i].length < 4))
        {
            for (var j=0; j<players.length; j++)
            {
                var player = players[j];
                if ((player == this) && !player.getHasRow())
                {
                    var score = player.getHand().scoreRow(theRows[i], theCard);
                    myRowScore = score.after - score.before;
                }
                else if (!player.getHasRow())
                {
                    var score = player.getHand().scoreRow(theRows[i], theCard);
                    theirRowScore += (score.after - score.before);
                    if (theirRowScore < theirWorstScore)
                    {
                        theirWorstScore = theirRowScore;
                        worstRow = i;
                    }
                    if (theirRowScore > theirBestScore)
                    {
                        theirBestScore = theirRowScore;
                    }
                }
            }

            // If somebody can do better than me by this row placement,
            // then we knock that placement down a bit in hopes that
            // something else bubbles to the top.
            if (theirBestScore > myRowScore)
            {
                if ((theCard.getColor() == 'wild') || (theCard.getColor() == 'plus2'))
                {
                    myRowScore -= theirBestScore;
                }
                else
                {
                    myRowScore -= 5;
                }
            }

            // when a pair is formed by placing this card, it's usually good
            // for everybody so make the placement of a card into arow that already
            // contains a card of the same color a bad thing.
            for (var j=1; j<theRows[i].length; j++)
            {
                // for every card that matches the pending card we dock the
                // row (don't want to form collections)
                if (theRows[i][j].getColor() == theCard.getColor())
                {
                    //alert("Souring match in row myRowScore=" + myRowScore);
                    myRowScore = -1;
                }
            }

            rowScores.push({ myScore: myRowScore, theirScore: theirRowScore, rowIndex: i });
        }
    }


    rowScores.sort(this.sortRowScores);

    // The first row is the initial default in hopes that it's the row
    // I benefit from
    var rowScore = rowScores[0];

    if (rowScore.myScore < 1)
    {
        // I don't benefit from this row so instead choose the last row
        // since we sort my best row first
        rowScore = rowScores[rowScores.length-1];
    }

    return rowScore.rowIndex;
}


ComputerPlayer.prototype.sortRowScores = function(r1, r2)
{
    if (r1 == null)
    {
        return 1;
    }

    if (r2 == null)
    {
        return -1;
    }

    if (r1.myScore > r2.myScore)
    {
        return -1;
    }

    if (r1.myScore < r2.myScore)
    {
        return 1;
    }

    return r1.theirScore - r2.theirScore;
}


