World Congress Review: Part 1 (Standard Mechanics)

Stalker0

Baller Magnus
Joined
Dec 31, 2005
Messages
10,912
This thread is meant to be a deep dive into the World Congress. This first part will deal with the general structure of the congress. Part 2 will look at the various proposals individually, and part 3 will look at Diplomatic Victory.


A victim of our success
There has been recent grumbling about the congress, and I think a lot of that is a reflection of our success with the AI. The AI has learned to play the WC very well, they are very human like in their responses now, and know how to counterplay a lot of the key moves in the congress. But with that comes frustration, it has become harder and harder to make real motion within the congress, and it feels like its literally you vs the world in many cases.

The info gap - The Guessing Game of Votes
In theory, the proposal "flavors" are supposed to give the human so general info about what proposals an AI values. If I have 2 proposals, one has a +1000 with the AI, and hte other 0, I would expect the AI to throw the vast majority of its votes into the first proposal, and almost nothing into the second. But commonly this is not how it works, because the AI takes into account a lot of other factors into its final vote, it might actually vote no using most of its votes on the 2nd proposal.

As a result, there really isn't any way to try and "vote with the group", its just random guessing. And so you often just have to throw all your votes at the wall and hope they stick, spreading your votes out is a complete "finger in the air" operation. Winning a vote doesn't feel like something you worked out, it just feels like you either had the votes to force it through, or you got lucky it passed.

Vote Trades - What Vote Trades?
Vote trading is in theory a way to really "work" the WC, but there are a few problems here. The first one is the barrier of entry....the diplomat. Giving up a spy is a big expenditure, especially since I don't know what the vote trade would look like ahead of time cost wise. So I have to commit a diplomat and "hope" I get a reasonable deal. That's just too much cost.

Second, often the vote trade costs are simply too high to be worth it. The AI will often ask for 100s of GPT in trades for even a few votes...its just insanity.

The Repeal Deal
As the proposals add up, so do the repeals. And it can get onerous in later congresses as you try to make ground and people are constantly throwing out repeals to trip you up. On the flip side, having to spend a proposal slot to undo someone else feels a bit lame, and it turns the WC into this quagmire state where nothing is getting done.


Ideas for Fixes
With my notes laid out, here are some ideas I may polish into proposals for the future congress.

The "Halftime" WC Congress
Currently every voting cycle, everyone puts in all their votes, and then we see who wins. And while that is how real votes work mechanically....that's really not how they work politically. The politicans have huge info networks to get insights into votes, by the time the vote actually hits the floor, most savvy politicians already know how that vote is going to go. While that is hard to simulate in the game, there is a way to mirror that a bit that would also help MP players as well.

The Idea: Each congress cycle is actually two votes. In the first congress (at the half way point of the cycle), you commit half your total number of current votes. So that means you now know the results of this commitment for the remaining half of the cycle. This gives you some insight into how people are voting, and you can then wheel and deal with this info in mind, completing your vote in the 2nd vote (which is the remaining half of your votes +/- any votes you gained or lost since the last cycle).

Ex: I have 20 votes going into a late game WC (10 turn cycle). At the 5 turn mark, I have to commit 10 of my votes to the proposals (half of 20). At the 10 turn mark, lets say I lost 2 votes due to CS allies I have lost. I now get 10 votes - the 2 I lost = 8 votes.

Vote Trades are Always On and now in Packages

In this idea, every civ receives a "vote package" they can give to each civ (which is 2 votes). This assumes of course they have at least 2 votes available. If they have a diplomat with that civ, they receive 1 additional vote package per spy level (aka 8 total votes with a max level diplomat + the base 2).

Advantages:
  • This gives you more flexibility with your vote trades. You can ask for 2 votes on proposal 1, 4 votes on proposal 2, etc etc. Its not all or nothing.
  • Removing the hard cost of a diplomat will greatly increase vote trading options.
  • You now can preview the cost of votes trades. If you see that Civ A is willing to trade their votes at a pretty reasonable price, then you can send your diplomat to them to negotiate for even more votes....as opposed to guessing what the cost might look like before sending in the diplomat.

Remove Repeals - Give Proposals Expiration Times
In this idea, repeals are no longer a thing. Instead, proposals with a duration just have a set amount of turns that they last. This is both a way to balance certain proposals, but it also removes all of the repeal headache, and so keeps the WC moving forward.

As a bonus option (if it can be implemented), it would be cool if a new GD bulb option allowed you to extend the cooldowns of certain proposals, but that might be tricky on the UI side.
 
The info gap - The Guessing Game of Votes
In theory, the proposal "flavors" are supposed to give the human so general info about what proposals an AI values. If I have 2 proposals, one has a +1000 with the AI, and hte other 0, I would expect the AI to throw the vast majority of its votes into the first proposal, and almost nothing into the second. But commonly this is not how it works, because the AI takes into account a lot of other factors into its final vote, it might actually vote no using most of its votes on the 2nd proposal.

As a result, there really isn't any way to try and "vote with the group", its just random guessing. And so you often just have to throw all your votes at the wall and hope they stick, spreading your votes out is a complete "finger in the air" operation. Winning a vote doesn't feel like something you worked out, it just feels like you either had the votes to force it through, or you got lucky it passed.
The AI takes into account two other things (besides how a proposal affects them).

1) Their alignment with the proposer:
Code:
        // == Alignment with Proposer ==
        if (eProposer != NO_PLAYER)
        {
            AlignmentLevels eAlignment = EvaluateAlignment(eProposer, false);
            switch (eAlignment)
            {
            case ALIGNMENT_SELF:
            case ALIGNMENT_LIBERATOR:
            case ALIGNMENT_TEAMMATE:
            case ALIGNMENT_LEADER:
                iScore += 150;
                break;
            case ALIGNMENT_ALLY:
                iScore += 120;
                break;
            case ALIGNMENT_CONFIDANT:
                iScore += 80;
                break;
            case ALIGNMENT_FRIEND:
                iScore += 40;
                break;
            case ALIGNMENT_RIVAL:
                iScore += -40;
                break;
            case ALIGNMENT_HATRED:
                iScore += -80;
                break;
            case ALIGNMENT_ENEMY:
            case ALIGNMENT_WAR:
                iScore += -120;
                break;
            default:
                break;
            }
        }

And for each other civ, based on how much they like them, they'll add a % of the score for that proposal. The % added is considerably large; it's not uncommon to have 160% 80% of the score added for just one civ in a standard size game. I believe this clearly needs revision, and is a big part of the voting issue.
Code:
            int iDiploScore = 0;
            int iTotalDiploScore = 0;
            int iDisputeLevel = 0;
            int iNumCivs = 1;
            for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++)
            {
                PlayerTypes eLoopPlayer = (PlayerTypes)iPlayerLoop;
                if (pLeague->CanEverVote(eLoopPlayer) && GET_PLAYER(eLoopPlayer).GetID() != GetPlayer()->GetID() && GET_PLAYER(eLoopPlayer).isAlive() && GET_PLAYER(eLoopPlayer).getNumCities() > 0)
                {
                    iDiploScore = 0;
                    if (GetPlayer()->GetDiplomacyAI()->GetCivOpinion(eLoopPlayer) != NO_CIV_OPINION)
                    {
                        iDiploScore += (GetPlayer()->GetDiplomacyAI()->GetCivOpinion(eLoopPlayer) - CIV_OPINION_NEUTRAL) * 8;
                    }
                    if (GetPlayer()->GetDiplomacyAI()->GetMostValuableFriend() == eLoopPlayer || GetPlayer()->GetDiplomacyAI()->GetMostValuableAlly() == eLoopPlayer)
                    {
                        iDiploScore += 8;
                    }
                    if (GetPlayer()->GetDiplomacyAI()->GetPrimeLeagueAlly() == eLoopPlayer)
                    {
                        iDiploScore += 8;
                    }
                    if (GetPlayer()->GetDiplomacyAI()->GetBiggestCompetitor() == eLoopPlayer)
                    {
                        iDiploScore -= 6;
                    }
                    if (GetPlayer()->GetDiplomacyAI()->GetPrimeLeagueCompetitor() == eLoopPlayer)
                    {
                        iDiploScore -= 6;
                    }
                    iDisputeLevel = max((int)pDiplo->GetVictoryDisputeLevel(eLoopPlayer), (int)pDiplo->GetVictoryBlockLevel(eLoopPlayer));
                    if (iDisputeLevel > 0)
                    {
                        iDiploScore -= iDisputeLevel * 6;
                    }
                    AlignmentLevels eAlignment = EvaluateAlignment(eLoopPlayer, false);
                    switch (eAlignment)
                    {
                    case ALIGNMENT_TEAMMATE:
                    case ALIGNMENT_LIBERATOR:
                    case ALIGNMENT_LEADER:
                        iDiploScore = 100;
                        break;
                    case ALIGNMENT_ALLY:
                        iDiploScore += 24;
                        break;
                    case ALIGNMENT_CONFIDANT:
                        iDiploScore += 16;
                        break;
                    case ALIGNMENT_FRIEND:
                        iDiploScore += 8;
                        break;
                    case ALIGNMENT_RIVAL:
                        iDiploScore += -8;
                        break;
                    case ALIGNMENT_HATRED:
                        iDiploScore += -16;
                        break;
                    case ALIGNMENT_ENEMY:
                    case ALIGNMENT_WAR:
                        iDiploScore += -24;
                        break;
                    default:
                        break;
                    }

                    iDiploScore *= GET_PLAYER(eLoopPlayer).GetLeagueAI()->ScoreVoteChoiceYesNo(pProposal, iChoice, bEnact, /*bConsiderGlobal*/ false, /*bForSelf*/ false);
                    iDiploScore /= 100;
                    iTotalDiploScore += iDiploScore;
                    iNumCivs++;
                }
            }
            iTotalDiploScore *= 16;
            iTotalDiploScore /= max(1,10 + iNumCivs);
            iScore += iTotalDiploScore;

If you're curious how alignment is calculated:
Spoiler :

Code:
// Calculate how much we think our interests are aligned with ePlayer, based on ideology, liberation, past voting patterns, etc.
CvLeagueAI::AlignmentLevels CvLeagueAI::EvaluateAlignment(PlayerTypes ePlayer, bool bIgnoreWar)
{
    int iAlignment = 0;
    CvDiplomacyAI* pDiplo = GetPlayer()->GetDiplomacyAI();

    if (ePlayer == NO_PLAYER||!GET_PLAYER(ePlayer).isAlive()||GET_PLAYER(ePlayer).isMinorCiv())
    {
        return ALIGNMENT_NEUTRAL;
    }
    if (GetPlayer()->GetID() == ePlayer)
    {
        return ALIGNMENT_SELF;
    }
    if (GetPlayer()->getTeam() == GET_PLAYER(ePlayer).getTeam())
    {
        if (GET_TEAM(GetPlayer()->getTeam()).getLeaderID() == ePlayer)
        {
            return ALIGNMENT_LEADER;
        }
        else
        {
            return ALIGNMENT_TEAMMATE;
        }
    }
    if (GET_TEAM(GetPlayer()->getTeam()).isAtWar(GET_PLAYER(ePlayer).getTeam()))
    {
        if (!bIgnoreWar && pDiplo->IsWantsToConquer(ePlayer))
        {
            return ALIGNMENT_WAR;
        }
        else
        {
            if (pDiplo->IsPhonyWar(ePlayer))
            {
                iAlignment -= 1;
            }
            else
            {
                iAlignment -= 2;
            }
        }
    }


    bool bUntrustworthy = pDiplo->IsUntrustworthy(ePlayer);

    // Ideology
    PolicyBranchTypes eOurIdeology = GetPlayer()->GetPlayerPolicies()->GetLateGamePolicyTree();
    PolicyBranchTypes eTheirIdeology = GET_PLAYER(ePlayer).GetPlayerPolicies()->GetLateGamePolicyTree();
    if (eOurIdeology != NO_POLICY_BRANCH_TYPE && eTheirIdeology != NO_POLICY_BRANCH_TYPE)
    {
        int iIdeologyMod = 0;

        if (eOurIdeology == eTheirIdeology)
        {
            iIdeologyMod += 2;
        }
        else if (!pDiplo->IsIgnoreIdeologyDifferences(ePlayer))
        {
            iIdeologyMod -= 2;
        }

        CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague();
        if (pLeague != NULL)
        {
            // Is this the World Ideology?
            if ((pLeague->GetPressureForIdeology(eOurIdeology) > 0) || (pLeague->GetPressureForIdeology(eTheirIdeology) > 0))
            {
                iIdeologyMod *= 2;
            }
        }

        // Are either of us a vassal?
        if (GetPlayer()->IsVassalOfSomeone())
        {
            iIdeologyMod /= 2;
        }
        if (GET_PLAYER(ePlayer).IsVassalOfSomeone())
        {
            iIdeologyMod /= 2;
        }

        iAlignment += iIdeologyMod;
    }

    // Religion
    ReligionTypes eOurReligion = GetPlayer()->GetReligions()->GetStateReligion(false);
    ReligionTypes eTheirReligion = GET_PLAYER(ePlayer).GetReligions()->GetStateReligion(false);
    if (eOurReligion != NO_RELIGION && eTheirReligion != NO_RELIGION)
    {
        int iReligionMod = 0;

        if (eOurReligion == eTheirReligion && !pDiplo->IsHolyCityCapturedBy(ePlayer))
        {
            iReligionMod += 1;
        }
        else if (!pDiplo->IsIgnoreReligionDifferences(ePlayer))
        {
            iReligionMod -= 1;
        }

        // Is this the World Religion?
        if ((GC.getGame().GetGameLeagues()->GetReligionSpreadStrengthModifier(ePlayer, eTheirReligion) > 0) || (GC.getGame().GetGameLeagues()->GetReligionSpreadStrengthModifier(GetPlayer()->GetID(), eOurReligion) > 0))
        {
            iReligionMod *= 2;
        }

        iAlignment += iReligionMod;
    }

    // Did they help us become host?
    if (pDiplo->GetSupportedMyHostingScore(ePlayer) != 0)
    {
        if (pDiplo->GetSupportedMyHostingScore(ePlayer) < -40)
        {
            iAlignment += 2;
        }
        else
        {
            iAlignment += 1;
        }
    }

    // Do we (dis)like their proposals or votes?
    if (pDiplo->GetLikedTheirProposalValue(ePlayer) < 0)
    {
        if (pDiplo->GetLikedTheirProposalValue(ePlayer) < /*-30*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL))
        {
            iAlignment += 2;
        }
        else
        {
            iAlignment += 1;
        }
    }
    else if (pDiplo->GetLikedTheirProposalValue(ePlayer) > 0)
    {
        if (pDiplo->GetLikedTheirProposalValue(ePlayer) > /*30*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL))
        {
            iAlignment -= 2;
        }
        else
        {
            iAlignment -= 1;
        }
    }

    // Have they supported our proposals?
    int iSupportedOurProposalScore = pDiplo->GetSupportedOurProposalScore(ePlayer);
    if (iSupportedOurProposalScore > 0)
    {
        if (pDiplo->IsFoiledOurProposalAndThenSupportedUs(ePlayer))
        {
            iAlignment -= 1;
        }
        else
        {
            iAlignment -= 2;
        }
    }
    else if (iSupportedOurProposalScore < 0)
    {
        if (pDiplo->IsSupportedOurProposalAndThenFoiledUs(ePlayer))
        {
            iAlignment += 1;
        }
        else
        {
            iAlignment += 2;
        }
    }

    // Have they (un)sanctioned us?
    if (pDiplo->HasEverSanctionedUs(ePlayer))
    {
        iAlignment -= 2;
    }
    else if (pDiplo->HasTriedToSanctionUs(ePlayer))
    {
        iAlignment -= 1;
    }
    if (pDiplo->HasEverUnsanctionedUs(ePlayer))
    {
        iAlignment += 2;
    }
    else if (pDiplo->HasTriedToUnsanctionUs(ePlayer))
    {
        iAlignment += 1;
    }

    // Finally, consider a potentially decisive factor: voting history!
    // This is intentionally powerful compared to other factors for three reasons: it grants the player more control over their diplomacy (fun/strategy), it takes a long time/major support to get to +10 (dedication), and it often requires sacrificing the player's preferred vote choices (tradeoff)
    iAlignment += /*-10 to 10*/ pDiplo->GetVotingHistoryScore(ePlayer) / /*240*/ max((GD_INT_GET(VOTING_HISTORY_SCORE_MAX) / GD_INT_GET(VOTING_HISTORY_SCORE_LEAGUE_ALIGNMENT_SCALER)), 1);

    // DoF or Denounce
    if (pDiplo->IsDenouncedPlayer(ePlayer) || pDiplo->IsDenouncedByPlayer(ePlayer))
    {
        iAlignment -= 2;
    }
    else if ((pDiplo->IsDoFAccepted(ePlayer) || pDiplo->IsWantsDoFWithPlayer(ePlayer)) && !pDiplo->IsWantsToEndDoFWithPlayer(ePlayer))
    {
        switch (pDiplo->GetDoFType(ePlayer))
        {
        case DOF_TYPE_UNTRUSTWORTHY:
        case DOF_TYPE_NEW:
            break;
        case DOF_TYPE_FRIENDS:
            iAlignment += 1;
            break;
        case DOF_TYPE_ALLIES:
            iAlignment += 2;
            break;
        case DOF_TYPE_BATTLE_BROTHERS:
            iAlignment += 3;
            break;
        }
    }

    // Defensive Pact?
    if (pDiplo->IsHasDefensivePact(ePlayer) && !pDiplo->IsWantsToEndDefensivePactWithPlayer(ePlayer))
    {
        iAlignment += 2;
    }

    // Current vassal?
    if (pDiplo->IsVassal(ePlayer))
    {
        VassalTreatmentTypes eTreatmentLevel = pDiplo->GetVassalTreatmentLevel(ePlayer);

        if (pDiplo->IsVoluntaryVassalage(ePlayer))
        {
            switch (eTreatmentLevel)
            {
            case NO_VASSAL_TREATMENT:
                UNREACHABLE();
            case VASSAL_TREATMENT_CONTENT:
                iAlignment += 4;
                break;
            case VASSAL_TREATMENT_DISAGREE:
                iAlignment += 2;
                break;
            case VASSAL_TREATMENT_MISTREATED:
                iAlignment -= 0;
                break;
            case VASSAL_TREATMENT_UNHAPPY:
                iAlignment -=  2;
                break;
            case VASSAL_TREATMENT_ENSLAVED:
                iAlignment -=  4;
                break;
            }
        }
        else
        {
            switch (eTreatmentLevel)
            {
            case NO_VASSAL_TREATMENT:
                UNREACHABLE();
            case VASSAL_TREATMENT_CONTENT:
                iAlignment += 2;
                break;
            case VASSAL_TREATMENT_DISAGREE:
                iAlignment -= 1;
                break;
            case VASSAL_TREATMENT_MISTREATED:
                iAlignment -=  2;
                break;
            case VASSAL_TREATMENT_UNHAPPY:
                iAlignment -=  3;
                break;
            case VASSAL_TREATMENT_ENSLAVED:
                iAlignment -=  5;
                break;
            }
        }
    }
    else
    {
        // Previous vassalage?
        if (pDiplo->IsMasterLiberatedMeFromVassalage(ePlayer))
        {
            iAlignment += 2;
        }
        else if (pDiplo->IsHappyAboutPlayerVassalagePeacefullyRevoked(ePlayer))
        {
            iAlignment += 1;
        }
        else if (pDiplo->IsAngryAboutPlayerVassalageForcefullyRevoked(ePlayer))
        {
            iAlignment -= 1;
        }
    }

    // Competing for City-States?
    iAlignment -= pDiplo->GetMinorCivDisputeLevel(ePlayer);


    switch (pDiplo->GetCivApproach(ePlayer))
    {
    case CIV_APPROACH_WAR:
    case CIV_APPROACH_HOSTILE:
        iAlignment -= 2;
        break;
    case CIV_APPROACH_DECEPTIVE:
    case CIV_APPROACH_GUARDED:
        iAlignment -= 1;
        break;
    case NO_CIV_APPROACH:
    case CIV_APPROACH_AFRAID:
    case CIV_APPROACH_NEUTRAL:
        iAlignment += 0;
        break;
    case CIV_APPROACH_FRIENDLY:
        iAlignment += 1;
        break;
    }

    if (pDiplo->GetWarmongerThreat(ePlayer) > 0)
    {
        iAlignment -= pDiplo->GetWarmongerThreat(ePlayer);
    }
    if (pDiplo->GetCoopWarScore(ePlayer) > 0)
    {
        iAlignment += pDiplo->GetCoopWarScore(ePlayer);
    }

    // Backstabber?
    if (bUntrustworthy)
    {
        iAlignment -= 4;

    }
    // Liberator?
    if (pDiplo->IsPlayerLiberatedCapital(ePlayer) || pDiplo->IsPlayerLiberatedHolyCity(ePlayer) || pDiplo->WasResurrectedBy(ePlayer))
    {
        if (!pDiplo->IsCompetingForVictory() && !bUntrustworthy && !GetPlayer()->IsAtWarWith(ePlayer))
        {
            iAlignment += 4;
        }

        else
        {
            iAlignment += 2;
        }
    }
    if (!GetPlayer()->IsAtWarWith(ePlayer) && pDiplo->GetNumCitiesLiberatedBy(ePlayer) > 0)
    {
        iAlignment += pDiplo->GetNumCitiesLiberatedBy(ePlayer);
        if (pDiplo->IsPlayerReturnedCapital(ePlayer) || pDiplo->IsPlayerReturnedHolyCity(ePlayer))
        {
            iAlignment += 1;
        }
    }

    // Captured some of our cities before?
    if (pDiplo->GetNumCitiesCapturedBy(ePlayer) > 0)
    {
        iAlignment -= pDiplo->GetNumCitiesCapturedBy(ePlayer);
    }

    int iDisputeLevel = max((int)pDiplo->GetVictoryDisputeLevel(ePlayer), (int)pDiplo->GetVictoryBlockLevel(ePlayer));
    if (iDisputeLevel > 0)
    {
        iAlignment -= iDisputeLevel;
    }

    AlignmentLevels eAlignment = ALIGNMENT_NEUTRAL;
    if (iAlignment < -8)
    {
        eAlignment = ALIGNMENT_ENEMY;
    }
    else if (iAlignment < -5)
    {
        eAlignment = ALIGNMENT_HATRED;
    }
    else if (iAlignment < -2)
    {
        eAlignment = ALIGNMENT_RIVAL;
    }
    else if (iAlignment <= 2)
    {
        eAlignment = ALIGNMENT_NEUTRAL;
    }
    else if (iAlignment <= 5)
    {
        eAlignment = ALIGNMENT_FRIEND;
    }
    else if (iAlignment <= 8)
    {
        eAlignment = ALIGNMENT_CONFIDANT;
    }
    else if (pDiplo->WasResurrectedBy(ePlayer))
    {
        eAlignment = ALIGNMENT_LIBERATOR;
    }
    else
    {
        eAlignment = ALIGNMENT_ALLY;
    }
    return eAlignment;
}
 
Last edited:
The AI takes into account two other things (besides how a proposal affects them).

1) Their alignment with the proposer:
Code:
        // == Alignment with Proposer ==
        if (eProposer != NO_PLAYER)
        {
            AlignmentLevels eAlignment = EvaluateAlignment(eProposer, false);
            switch (eAlignment)
            {
            case ALIGNMENT_SELF:
            case ALIGNMENT_LIBERATOR:
            case ALIGNMENT_TEAMMATE:
            case ALIGNMENT_LEADER:
                iScore += 150;
                break;
            case ALIGNMENT_ALLY:
                iScore += 120;
                break;
            case ALIGNMENT_CONFIDANT:
                iScore += 80;
                break;
            case ALIGNMENT_FRIEND:
                iScore += 40;
                break;
            case ALIGNMENT_RIVAL:
                iScore += -40;
                break;
            case ALIGNMENT_HATRED:
                iScore += -80;
                break;
            case ALIGNMENT_ENEMY:
            case ALIGNMENT_WAR:
                iScore += -120;
                break;
            default:
                break;
            }
        }

And for each other civ, based on how much they like them, they'll add a % of the score for that proposal. The % added is considerably large; it's not uncommon to have 160% of the score added for just one civ in a standard size game. I believe this clearly needs revision, and is a big part of the voting issue.
Code:
            int iDiploScore = 0;
            int iTotalDiploScore = 0;
            int iDisputeLevel = 0;
            int iNumCivs = 1;
            for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++)
            {
                PlayerTypes eLoopPlayer = (PlayerTypes)iPlayerLoop;
                if (pLeague->CanEverVote(eLoopPlayer) && GET_PLAYER(eLoopPlayer).GetID() != GetPlayer()->GetID() && GET_PLAYER(eLoopPlayer).isAlive() && GET_PLAYER(eLoopPlayer).getNumCities() > 0)
                {
                    iDiploScore = 0;
                    if (GetPlayer()->GetDiplomacyAI()->GetCivOpinion(eLoopPlayer) != NO_CIV_OPINION)
                    {
                        iDiploScore += (GetPlayer()->GetDiplomacyAI()->GetCivOpinion(eLoopPlayer) - CIV_OPINION_NEUTRAL) * 8;
                    }
                    if (GetPlayer()->GetDiplomacyAI()->GetMostValuableFriend() == eLoopPlayer || GetPlayer()->GetDiplomacyAI()->GetMostValuableAlly() == eLoopPlayer)
                    {
                        iDiploScore += 8;
                    }
                    if (GetPlayer()->GetDiplomacyAI()->GetPrimeLeagueAlly() == eLoopPlayer)
                    {
                        iDiploScore += 8;
                    }
                    if (GetPlayer()->GetDiplomacyAI()->GetBiggestCompetitor() == eLoopPlayer)
                    {
                        iDiploScore -= 6;
                    }
                    if (GetPlayer()->GetDiplomacyAI()->GetPrimeLeagueCompetitor() == eLoopPlayer)
                    {
                        iDiploScore -= 6;
                    }
                    iDisputeLevel = max((int)pDiplo->GetVictoryDisputeLevel(eLoopPlayer), (int)pDiplo->GetVictoryBlockLevel(eLoopPlayer));
                    if (iDisputeLevel > 0)
                    {
                        iDiploScore -= iDisputeLevel * 6;
                    }
                    AlignmentLevels eAlignment = EvaluateAlignment(eLoopPlayer, false);
                    switch (eAlignment)
                    {
                    case ALIGNMENT_TEAMMATE:
                    case ALIGNMENT_LIBERATOR:
                    case ALIGNMENT_LEADER:
                        iDiploScore = 100;
                        break;
                    case ALIGNMENT_ALLY:
                        iDiploScore += 24;
                        break;
                    case ALIGNMENT_CONFIDANT:
                        iDiploScore += 16;
                        break;
                    case ALIGNMENT_FRIEND:
                        iDiploScore += 8;
                        break;
                    case ALIGNMENT_RIVAL:
                        iDiploScore += -8;
                        break;
                    case ALIGNMENT_HATRED:
                        iDiploScore += -16;
                        break;
                    case ALIGNMENT_ENEMY:
                    case ALIGNMENT_WAR:
                        iDiploScore += -24;
                        break;
                    default:
                        break;
                    }

                    iDiploScore *= GET_PLAYER(eLoopPlayer).GetLeagueAI()->ScoreVoteChoiceYesNo(pProposal, iChoice, bEnact, /*bConsiderGlobal*/ false, /*bForSelf*/ false);
                    iDiploScore /= 100;
                    iTotalDiploScore += iDiploScore;
                    iNumCivs++;
                }
            }
            iTotalDiploScore *= 16;
            iTotalDiploScore /= max(1,10 + iNumCivs);
            iScore += iTotalDiploScore;

If you're curious how alignment is calculated:
Spoiler :

Code:
// Calculate how much we think our interests are aligned with ePlayer, based on ideology, liberation, past voting patterns, etc.
CvLeagueAI::AlignmentLevels CvLeagueAI::EvaluateAlignment(PlayerTypes ePlayer, bool bIgnoreWar)
{
    int iAlignment = 0;
    CvDiplomacyAI* pDiplo = GetPlayer()->GetDiplomacyAI();

    if (ePlayer == NO_PLAYER||!GET_PLAYER(ePlayer).isAlive()||GET_PLAYER(ePlayer).isMinorCiv())
    {
        return ALIGNMENT_NEUTRAL;
    }
    if (GetPlayer()->GetID() == ePlayer)
    {
        return ALIGNMENT_SELF;
    }
    if (GetPlayer()->getTeam() == GET_PLAYER(ePlayer).getTeam())
    {
        if (GET_TEAM(GetPlayer()->getTeam()).getLeaderID() == ePlayer)
        {
            return ALIGNMENT_LEADER;
        }
        else
        {
            return ALIGNMENT_TEAMMATE;
        }
    }
    if (GET_TEAM(GetPlayer()->getTeam()).isAtWar(GET_PLAYER(ePlayer).getTeam()))
    {
        if (!bIgnoreWar && pDiplo->IsWantsToConquer(ePlayer))
        {
            return ALIGNMENT_WAR;
        }
        else
        {
            if (pDiplo->IsPhonyWar(ePlayer))
            {
                iAlignment -= 1;
            }
            else
            {
                iAlignment -= 2;
            }
        }
    }


    bool bUntrustworthy = pDiplo->IsUntrustworthy(ePlayer);

    // Ideology
    PolicyBranchTypes eOurIdeology = GetPlayer()->GetPlayerPolicies()->GetLateGamePolicyTree();
    PolicyBranchTypes eTheirIdeology = GET_PLAYER(ePlayer).GetPlayerPolicies()->GetLateGamePolicyTree();
    if (eOurIdeology != NO_POLICY_BRANCH_TYPE && eTheirIdeology != NO_POLICY_BRANCH_TYPE)
    {
        int iIdeologyMod = 0;

        if (eOurIdeology == eTheirIdeology)
        {
            iIdeologyMod += 2;
        }
        else if (!pDiplo->IsIgnoreIdeologyDifferences(ePlayer))
        {
            iIdeologyMod -= 2;
        }

        CvLeague* pLeague = GC.getGame().GetGameLeagues()->GetActiveLeague();
        if (pLeague != NULL)
        {
            // Is this the World Ideology?
            if ((pLeague->GetPressureForIdeology(eOurIdeology) > 0) || (pLeague->GetPressureForIdeology(eTheirIdeology) > 0))
            {
                iIdeologyMod *= 2;
            }
        }

        // Are either of us a vassal?
        if (GetPlayer()->IsVassalOfSomeone())
        {
            iIdeologyMod /= 2;
        }
        if (GET_PLAYER(ePlayer).IsVassalOfSomeone())
        {
            iIdeologyMod /= 2;
        }

        iAlignment += iIdeologyMod;
    }

    // Religion
    ReligionTypes eOurReligion = GetPlayer()->GetReligions()->GetStateReligion(false);
    ReligionTypes eTheirReligion = GET_PLAYER(ePlayer).GetReligions()->GetStateReligion(false);
    if (eOurReligion != NO_RELIGION && eTheirReligion != NO_RELIGION)
    {
        int iReligionMod = 0;

        if (eOurReligion == eTheirReligion && !pDiplo->IsHolyCityCapturedBy(ePlayer))
        {
            iReligionMod += 1;
        }
        else if (!pDiplo->IsIgnoreReligionDifferences(ePlayer))
        {
            iReligionMod -= 1;
        }

        // Is this the World Religion?
        if ((GC.getGame().GetGameLeagues()->GetReligionSpreadStrengthModifier(ePlayer, eTheirReligion) > 0) || (GC.getGame().GetGameLeagues()->GetReligionSpreadStrengthModifier(GetPlayer()->GetID(), eOurReligion) > 0))
        {
            iReligionMod *= 2;
        }

        iAlignment += iReligionMod;
    }

    // Did they help us become host?
    if (pDiplo->GetSupportedMyHostingScore(ePlayer) != 0)
    {
        if (pDiplo->GetSupportedMyHostingScore(ePlayer) < -40)
        {
            iAlignment += 2;
        }
        else
        {
            iAlignment += 1;
        }
    }

    // Do we (dis)like their proposals or votes?
    if (pDiplo->GetLikedTheirProposalValue(ePlayer) < 0)
    {
        if (pDiplo->GetLikedTheirProposalValue(ePlayer) < /*-30*/ GD_INT_GET(OPINION_WEIGHT_WE_LIKED_THEIR_PROPOSAL))
        {
            iAlignment += 2;
        }
        else
        {
            iAlignment += 1;
        }
    }
    else if (pDiplo->GetLikedTheirProposalValue(ePlayer) > 0)
    {
        if (pDiplo->GetLikedTheirProposalValue(ePlayer) > /*30*/ GD_INT_GET(OPINION_WEIGHT_WE_DISLIKED_THEIR_PROPOSAL))
        {
            iAlignment -= 2;
        }
        else
        {
            iAlignment -= 1;
        }
    }

    // Have they supported our proposals?
    int iSupportedOurProposalScore = pDiplo->GetSupportedOurProposalScore(ePlayer);
    if (iSupportedOurProposalScore > 0)
    {
        if (pDiplo->IsFoiledOurProposalAndThenSupportedUs(ePlayer))
        {
            iAlignment -= 1;
        }
        else
        {
            iAlignment -= 2;
        }
    }
    else if (iSupportedOurProposalScore < 0)
    {
        if (pDiplo->IsSupportedOurProposalAndThenFoiledUs(ePlayer))
        {
            iAlignment += 1;
        }
        else
        {
            iAlignment += 2;
        }
    }

    // Have they (un)sanctioned us?
    if (pDiplo->HasEverSanctionedUs(ePlayer))
    {
        iAlignment -= 2;
    }
    else if (pDiplo->HasTriedToSanctionUs(ePlayer))
    {
        iAlignment -= 1;
    }
    if (pDiplo->HasEverUnsanctionedUs(ePlayer))
    {
        iAlignment += 2;
    }
    else if (pDiplo->HasTriedToUnsanctionUs(ePlayer))
    {
        iAlignment += 1;
    }

    // Finally, consider a potentially decisive factor: voting history!
    // This is intentionally powerful compared to other factors for three reasons: it grants the player more control over their diplomacy (fun/strategy), it takes a long time/major support to get to +10 (dedication), and it often requires sacrificing the player's preferred vote choices (tradeoff)
    iAlignment += /*-10 to 10*/ pDiplo->GetVotingHistoryScore(ePlayer) / /*240*/ max((GD_INT_GET(VOTING_HISTORY_SCORE_MAX) / GD_INT_GET(VOTING_HISTORY_SCORE_LEAGUE_ALIGNMENT_SCALER)), 1);

    // DoF or Denounce
    if (pDiplo->IsDenouncedPlayer(ePlayer) || pDiplo->IsDenouncedByPlayer(ePlayer))
    {
        iAlignment -= 2;
    }
    else if ((pDiplo->IsDoFAccepted(ePlayer) || pDiplo->IsWantsDoFWithPlayer(ePlayer)) && !pDiplo->IsWantsToEndDoFWithPlayer(ePlayer))
    {
        switch (pDiplo->GetDoFType(ePlayer))
        {
        case DOF_TYPE_UNTRUSTWORTHY:
        case DOF_TYPE_NEW:
            break;
        case DOF_TYPE_FRIENDS:
            iAlignment += 1;
            break;
        case DOF_TYPE_ALLIES:
            iAlignment += 2;
            break;
        case DOF_TYPE_BATTLE_BROTHERS:
            iAlignment += 3;
            break;
        }
    }

    // Defensive Pact?
    if (pDiplo->IsHasDefensivePact(ePlayer) && !pDiplo->IsWantsToEndDefensivePactWithPlayer(ePlayer))
    {
        iAlignment += 2;
    }

    // Current vassal?
    if (pDiplo->IsVassal(ePlayer))
    {
        VassalTreatmentTypes eTreatmentLevel = pDiplo->GetVassalTreatmentLevel(ePlayer);

        if (pDiplo->IsVoluntaryVassalage(ePlayer))
        {
            switch (eTreatmentLevel)
            {
            case NO_VASSAL_TREATMENT:
                UNREACHABLE();
            case VASSAL_TREATMENT_CONTENT:
                iAlignment += 4;
                break;
            case VASSAL_TREATMENT_DISAGREE:
                iAlignment += 2;
                break;
            case VASSAL_TREATMENT_MISTREATED:
                iAlignment -= 0;
                break;
            case VASSAL_TREATMENT_UNHAPPY:
                iAlignment -=  2;
                break;
            case VASSAL_TREATMENT_ENSLAVED:
                iAlignment -=  4;
                break;
            }
        }
        else
        {
            switch (eTreatmentLevel)
            {
            case NO_VASSAL_TREATMENT:
                UNREACHABLE();
            case VASSAL_TREATMENT_CONTENT:
                iAlignment += 2;
                break;
            case VASSAL_TREATMENT_DISAGREE:
                iAlignment -= 1;
                break;
            case VASSAL_TREATMENT_MISTREATED:
                iAlignment -=  2;
                break;
            case VASSAL_TREATMENT_UNHAPPY:
                iAlignment -=  3;
                break;
            case VASSAL_TREATMENT_ENSLAVED:
                iAlignment -=  5;
                break;
            }
        }
    }
    else
    {
        // Previous vassalage?
        if (pDiplo->IsMasterLiberatedMeFromVassalage(ePlayer))
        {
            iAlignment += 2;
        }
        else if (pDiplo->IsHappyAboutPlayerVassalagePeacefullyRevoked(ePlayer))
        {
            iAlignment += 1;
        }
        else if (pDiplo->IsAngryAboutPlayerVassalageForcefullyRevoked(ePlayer))
        {
            iAlignment -= 1;
        }
    }

    // Competing for City-States?
    iAlignment -= pDiplo->GetMinorCivDisputeLevel(ePlayer);


    switch (pDiplo->GetCivApproach(ePlayer))
    {
    case CIV_APPROACH_WAR:
    case CIV_APPROACH_HOSTILE:
        iAlignment -= 2;
        break;
    case CIV_APPROACH_DECEPTIVE:
    case CIV_APPROACH_GUARDED:
        iAlignment -= 1;
        break;
    case NO_CIV_APPROACH:
    case CIV_APPROACH_AFRAID:
    case CIV_APPROACH_NEUTRAL:
        iAlignment += 0;
        break;
    case CIV_APPROACH_FRIENDLY:
        iAlignment += 1;
        break;
    }

    if (pDiplo->GetWarmongerThreat(ePlayer) > 0)
    {
        iAlignment -= pDiplo->GetWarmongerThreat(ePlayer);
    }
    if (pDiplo->GetCoopWarScore(ePlayer) > 0)
    {
        iAlignment += pDiplo->GetCoopWarScore(ePlayer);
    }

    // Backstabber?
    if (bUntrustworthy)
    {
        iAlignment -= 4;

    }
    // Liberator?
    if (pDiplo->IsPlayerLiberatedCapital(ePlayer) || pDiplo->IsPlayerLiberatedHolyCity(ePlayer) || pDiplo->WasResurrectedBy(ePlayer))
    {
        if (!pDiplo->IsCompetingForVictory() && !bUntrustworthy && !GetPlayer()->IsAtWarWith(ePlayer))
        {
            iAlignment += 4;
        }

        else
        {
            iAlignment += 2;
        }
    }
    if (!GetPlayer()->IsAtWarWith(ePlayer) && pDiplo->GetNumCitiesLiberatedBy(ePlayer) > 0)
    {
        iAlignment += pDiplo->GetNumCitiesLiberatedBy(ePlayer);
        if (pDiplo->IsPlayerReturnedCapital(ePlayer) || pDiplo->IsPlayerReturnedHolyCity(ePlayer))
        {
            iAlignment += 1;
        }
    }

    // Captured some of our cities before?
    if (pDiplo->GetNumCitiesCapturedBy(ePlayer) > 0)
    {
        iAlignment -= pDiplo->GetNumCitiesCapturedBy(ePlayer);
    }

    int iDisputeLevel = max((int)pDiplo->GetVictoryDisputeLevel(ePlayer), (int)pDiplo->GetVictoryBlockLevel(ePlayer));
    if (iDisputeLevel > 0)
    {
        iAlignment -= iDisputeLevel;
    }

    AlignmentLevels eAlignment = ALIGNMENT_NEUTRAL;
    if (iAlignment < -8)
    {
        eAlignment = ALIGNMENT_ENEMY;
    }
    else if (iAlignment < -5)
    {
        eAlignment = ALIGNMENT_HATRED;
    }
    else if (iAlignment < -2)
    {
        eAlignment = ALIGNMENT_RIVAL;
    }
    else if (iAlignment <= 2)
    {
        eAlignment = ALIGNMENT_NEUTRAL;
    }
    else if (iAlignment <= 5)
    {
        eAlignment = ALIGNMENT_FRIEND;
    }
    else if (iAlignment <= 8)
    {
        eAlignment = ALIGNMENT_CONFIDANT;
    }
    else if (pDiplo->WasResurrectedBy(ePlayer))
    {
        eAlignment = ALIGNMENT_LIBERATOR;
    }
    else
    {
        eAlignment = ALIGNMENT_ALLY;
    }
    return eAlignment;
}
Possibly the wrong thread to ask this, but does GetNumCitiesCapturedBy() return the AI's cities that are currently held by that player or every city that was ever captured by that player? If it's the former, does it also include cities bought through diplomacy, or conquered after a rebellion or from a third party, and does it account for razed cities? And if the latter, would it count a city that was captured, given back in the peace and captured again in a later war once or twice?
 
"
Remove Repeals - Give Proposals Expiration Times
In this idea, repeals are no longer a thing. Instead, proposals with a duration just have a set amount of turns that they last. This is both a way to balance certain proposals, but it also removes all of the repeal headache, and so keeps the WC moving forward.
"
Maybe we could have new session vote at the end of each proposal timer to keep it longer or not ? So the proposal stay in place until a "Do not keep the proposal" is voted ?
 
"
Maybe we could have new session vote at the end of each proposal timer to keep it longer or not ? So the proposal stay in place until a "Do not keep the proposal" is voted ?
this was my original thought but the issue is that over time the number of proposals to repeal would steadily increase, and so you would need a procedural change to address that.
 
The AI takes into account two other things (besides how a proposal affects them).
Yeah I honestly don't think think any of this should be a factor in vote consideration. I almost always vote how its going to affect me, and only in cases where I am really ambivalent do I go "well I will give it to my buddy to help them out".

Now in terms of proposals...absolutely I will consider how much it makes other civs like/hate me, but not for voting.
 
As an alternative to half-time votes (a mechanic which I find a bit clunky), what if we modified your idea for a multi-step congress to make it function more like how most "democratic" bodies function:
  1. Every player makes a proposal
  2. There is a preliminary vote on those proposals at the halfway point of the congress phase. Vote count is not taken into account here, instead each player can vote "yay," "nay," or "abstain" on each proposal. I see at least 5 options for how to use this vote: 1) every proposal that wins more yays than nays move to the final round, 2) the 3 proposals with the best yay/nay ratio move to the final round, 3) the 3 proposals with the most yay votes move to the final round, 4) the 3 proposals with the fewest nay votes move to the final round, or 5) of the proposals that win more yays than nays, the 3 that move on are those proposed by players with the highest scores/number of votes in the next round. Each option has its pros and cons but I think either 3 or 4 would be the best and cleanest.
  3. There is a final vote on the curated proposals, with each player having the same number of votes to use as they do now.
The key change here would be that all players, not just 3, would be able to make proposals, and that every player has roughly equal power to determine which proposals receive a final vote. The goal would be to 1) provide an extra source of information for the human player about the popularity of the proposals that make it to the final round, and 2) make the congress more about making proposals appealing than fighting for votes to ram through unpopular proposals. The current congress system is hyper-realist in the sense that it does not really facilitate or reward alliances or strategic partnerships that much and locks players into incessant conflict.

However, this system would also best be implemented with changes to make vote-buying more prevalent, and would allow you to buy both votes in the preliminary round and then votes in the final round, but also to trade preliminary round votes, i.e. I'll vote yay on your proposal if you vote yay on mine. More vote buying would help preserve the viability of a DV, which largely relies on ramming through unpopular proposals.

I have a few more thoughts on how to make my proposed system work its best, but I'll save them as I'm interested in what other people think about my basic idea.
 
Last edited:
Possibly the wrong thread to ask this, but does GetNumCitiesCapturedBy() return the AI's cities that are currently held by that player or every city that was ever captured by that player? If it's the former, does it also include cities bought through diplomacy, or conquered after a rebellion or from a third party, and does it account for razed cities? And if the latter, would it count a city that was captured, given back in the peace and captured again in a later war once or twice?
Goes up by +1 every time you capture a city. Recapturing the same city counts. Resets to 0 if you resurrect them. Nothing else changes it.

There is a separate function, GetNumOurCitiesOwnedBy(), which returns the number of the AI's originally founded cities that are currently in the possession of a player.
Yeah I honestly don't think think any of this should be a factor in vote consideration. I almost always vote how its going to affect me, and only in cases where I am really ambivalent do I go "well I will give it to my buddy to help them out".

Now in terms of proposals...absolutely I will consider how much it makes other civs like/hate me, but not for voting.
Alignment with the proposer I think should be removed as a factor.

Voting history, for or against, has a significant impact on diplomacy, and is intended to have a significant impact on the AI's World Congress alignment towards you. In practice, because the scoring is not well executed, voting is a crapshoot. I do think there is room to replace the "consider others' viewpoints" algorithm so that it's not a crapshoot but does take into account how others would be affected. I would need some ideas for this.

(Also I made an error, it's not 160% of the score that commonly gets added, it's 80%; but when multiplied by 6-7 other civs, that can still completely overpower how the resolution affects the AI.)

The vanilla behavior where the AI just votes selfishly all the time is very predictable, exploitable, and doesn't help the AI build bridges or avoid making enemies. I would say, at minimum, that the AI should take into account how many votes it has relative to others before deciding how to vote. If it can't pass something on their own, there are better and worse choices to make.
 
It feels you hit the nail on the head at identifying the problems with current World Congress, I completely agree with your remarks.
To give more visibility for players, wouldn't it be possible to have more detailed pop-up window when seeing who might vote for what?
 
Top Bottom