Developing Diplomacy

For AI and campaign script related discussions and questions
Post Reply
User avatar
NoQ
Special
Special
Posts: 6226
Joined: 24 Dec 2009, 11:35
Location: /var/zone

Developing Diplomacy

Post by NoQ »

I've just had some fun creating a framework for testing AI diplomacy scripts. It is currently a very rough pre-alpha, please do not read it too attentively, just wanted to share some fun :oops: i'll report again when i get serious.

The cool story: I wanted to teach AIs play well in the "allow alliances" game mode, but Per claims that no good diplomacy is possible to script up. I still was willing to try it, but it's too easy to make mistakes or allow unwanted exploits. So i need some way of testing things quickly without playing a whole wz game.

So i made a wrapper script (lets call it test.js) to wrap around the partial AI script i'm testing (lets call it diplomacy.js) in such a way that the second script could be included into the real AI without changes, while the first script emulates the game by calling the necessary functions and event handlers from the second script.

It all runs on rhino, a free command-line JavaScript engine. I just fire up the rhino console, say load("test.js"), it itself loads the diplomacy scripts, and then i call the simple JS functions provided by test.js:
  • h(i) - mark player i as a human (disable automatic behaviour);
  • m(i) - set i as the current player (needs to be marked as human first);
  • o(i) - offer alliance from current player to player i;
  • b(i) - break alliance from current player to player i;
  • s() - make some random player that is not marked as a human offer or break a random alliance.
Here is an example of a sample conversation:

Code: Select all

$ rhino
Rhino 1.7 release 4 2012 09 15
js> load("test.js")
me = 0
js> o(1)
0 befriends 1
1 befriends 0
js> s()
_____________________________
>>>> tick
<<<< tack
7 befriends 2
2 befriends 7
teams: [ 0 1 ] [ 2 7 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 8 ] [ 9 ] 
me = 0
js> o(3)
0 befriends 3
3 tells 0: You must break your alliance with 1 first.
js> s()
_____________________________
>>>> tick
<<<< tack
6 befriends 0
6 befriends 1
1 befriends 6
teams: [ 0 1 3 ] [ 2 7 ] [ 4 ] [ 5 ] [ 6 ] [ 8 ] [ 9 ] 
me = 0
Here is the test.js script that provides an environment for testing.

Code: Select all

// hide the internals from the user
(function(_global) {


var maxPlayers = 10; // set anything here

var arr = []; // emulating the alliance matrix
var me = 0; // a global representing "me" for every instance
var humans = []; // humans don't do stuff automatically

// initialize the arrays
for (var i = 0; i < maxPlayers; ++i) {
	humans[i] = false;
	arr[i] = [];
	for (var j = 0; j < maxPlayers; ++j)
		arr[i][j] = false;
}


/* for convenience */

function random(max) {
	return Math.floor(Math.random() * max);
}

function printme() {
	print("me = " + me);
}


/* new script API functions i want implemented */

function offerAlliance(j) {
	var i = me;
	print(i + " befriends " + j);
	arr[i][j] = true;
	for (me = 0; me < maxPlayers; ++me)
		if (!humans[me])
			eventAllianceOffered(i, j);
	me = i;
}

function breakAlliance(j) {
	var i = me;
	print(i + " betrays " + j);
	arr[i][j] = false;
	for (me = 0; me < maxPlayers; ++me)
		if (!humans[me])
			eventAllianceBroken(i, j);
	me = i;
}


/* emulating existing script API functions */

function allianceExistsBetween(i, j) {
	return arr[i][j];
}

function chat(to, message) {
	print(me + " tells " + to + ": " + message);
}

function _(str) {
	return str;
}


/* include the stuff we're testing */

eval(readFile("diplomacy.js"));


/* the main loop */

function step() {

	print("_____________________________");

	// emulate waiting for player reaction
	print(">>>> tick");
	for (me = 0; me < maxPlayers; ++me)
		if (!humans[me])
			doRelaxAlliances();

	// emulate player activity
	print("<<<< tack");
	me = random(maxPlayers);
	if (!humans[me])
		doRandomStuff();

	// print teams
	var str = "teams: "
	var printed = [];
	for (var i = 0; i < maxPlayers; ++i)
		printed[i] = false;
	for (var i = 0; i < maxPlayers; ++i) 
		if (!printed[i]) {
			str += "[ " + i + " ";
			for (var j = i; j < maxPlayers; ++j)
				if (!printed[j] && allianceExistsBetween(i, j)) {
					str += j + " ";
					printed[j] = true;
				}
			str += "] ";
		}
	print(str);
	m(0);
}

/* functions for using in the shell */
_global.h = function(i) { humans[i] = true; me = i; printme(); }
_global.m = function(i) { me = humans[i] ? i : me; printme(); }
_global.o = offerAlliance;
_global.b = breakAlliance;
_global.s = step;

// add a human player
h(0);

// end: hide the internals from the user
})(this);
Here is an example of the diplomacy.js script to be tested.

Code: Select all

// for convenience
function A(i, j) {
	return allianceExistsBetween(i, j);
}

function eventAllianceOffered(from, to) {

	// somebody says he loves us?
	// do the necessary blushing.
	if (me === to) {

		// nothing to do if we are already in love
		if (A(me, from))
			return;

		// what if from is a bad guy,
		// who deals with nasty people?
		for (var i = 0; i < maxPlayers; ++i)
			if (i != me && A(from, i) && !A(me, i)) {
				chat(from, _("You must break your alliance with " + i + " first."));
				return;
			}

		// otherwise, friendship is magic
		offerAlliance(from);

	}

}

function eventAllianceBroken(from, to) {

	// was it a reply? nothing special then
	if (!A(to, from))
		return;

}

function doRandomStuff() {

	var i = random(maxPlayers);

	// betray friends from time to time
	if (random(3) == 0)
		if (me !== i && A(me, i)) {
			breakAlliance(i);
			return;
		}

	// make friends most of the time
	if (me !== i && !A(i, me) && !A(me, i))
		for (var j = 0; j < maxPlayers; ++j)
			if (me !== j && (i === j || A(i, j)))
				offerAlliance(j);

}

function doRelaxAlliances() {

	// remove outdated alliance offers
	for (var i = 0; i < maxPlayers; ++i)
		if (i !== me && A(me, i) && !A(i, me))
			breakAlliance(i);

}
User avatar
aubergine
Professional
Professional
Posts: 3459
Joined: 10 Oct 2010, 00:58
Contact:

Re: Developing Diplomacy

Post by aubergine »

I've never really played MP and AIs in 3.x branch don't handle dynamic alliances (at least not the .js ones) - how exactly does dynamic alliances work? Specifically, what are the rules about who can be allied with who?

For example, in a 3 player game, player 0 can't be allied with both player 1 and player 2, because then nobody could attack player 0.

I'm just trying to work out sort of what the boundaries are or the scope of possibilities. Knowing that I'd feel much more confident about giving feedback.

However as a guess, I would imagine that there would need to be some mechanism that prevents everyone ganging up on a single player, or something that generally results in fairly even numbers of players in a team.

Note to avoid misinterpretation: I'm not saying dynamic alliance mode is a bad thing, I'm just saying I've had no experience of it as I don't play MP and I couldn't find any written information about how it manifests in the gameplay.
"Dedicated to discovering Warzone artefacts, and sharing them freely for the benefit of the community."
-- https://warzone.atlassian.net/wiki/display/GO
User avatar
NoQ
Special
Special
Posts: 6226
Joined: 24 Dec 2009, 11:35
Location: /var/zone

Re: Developing Diplomacy

Post by NoQ »

With dynamic alliances an alliance is a one-sided obligation of an arbitrary player to never shoot at any other arbitrary player. When both players befriend each other they get extra benefits like being able to share research and vision and joining forces without hurting each other.

Naturally you wouldn't like there to be situations when you are allied with somebody but he is not allied with you. But somebody needs to make the first step anyway.

P.S. In your example, if a player 0 has a mutual alliance to both 1 and 2 he instantly wins. I think this should be changed: the game should not end until only one player remains (:
User avatar
aubergine
Professional
Professional
Posts: 3459
Joined: 10 Oct 2010, 00:58
Contact:

Re: Developing Diplomacy

Post by aubergine »

Ah, so the share research/vision things only exist in dynamic alliances?! - no wonder I never saw them (JS API AIs can't accept alliance so I never see those options in ski mode).

I always thought the 'offer alliance' was just a way to say 'do you want to form an alliance' and only if other player accepted did a NAP get formed (between both players at same time). Why would you ever want a NAP with a player that doesn't reciprocate that NAP?

Also, I agree that the game should require a Highlander-style "There can be only one!" fight to the death.
"Dedicated to discovering Warzone artefacts, and sharing them freely for the benefit of the community."
-- https://warzone.atlassian.net/wiki/display/GO
Post Reply