Here’s the full code, so far. It plays Prisoner’s Dilemma like it’s RPS… a little odd. The game selection is hard-coded in. Making the game selectable is the next step.
What works well in this code is that the PC can ask any NPC to play, or be asked, and accept, or be turned down, and move processing and gaem logic needn’t be defined any further than the winning/losing -criteria definitions shown in the last post.
OTOH, NPCs pick moves strictly at random. Setting up NPC strategies is to do after making the gaem selectable, and not hard-coded.
[rant][code]#charset “us-ascii”
#include <adv3.h>
#include <en_us.h>
#include <file.h>
////////////////////////////////////////////////////////////////////////////////
prand: object { //“Gerhard’s generator”, an extremely simple algorithm that generates low-quality pseudorandom numbers.
seed = 10; // Use whatever value you want as initial seed.
ok(lim) // 0..(lim-1)
{
seed = (seed * 32719 + 3) % 32749;
// "\nok \nseed = <>. lim = <> \n seed % lim = <<seed % lim>>. \n (seed % lim) + 1 = <<(seed % lim )>> ";
return seed % lim;
}
n0(lim) // 1..lim
{
seed = (seed * 32719 + 3) % 32749;
// "\nn0 \nseed = <>. lim = <> \n seed % lim = <<seed % lim>>. \n (seed % lim) + 1 = <<(seed % lim ) + 1>> ";
return (seed % lim ) + 1;
}
}
;
////////////////////////////////////////////////////////////////////////////////
//////// THE GENERAL GAEM OBJECT ///////////////////////////////////////////////
GaemThing : object
gaemName = ''
moveNameV = 'move{s}'
p1 = nil
p2 = nil
p1move = ''
p2move = ''
p1result = ''
p2result = ''
outcome = ''
nolegalmoves = 0
legalmovesList = []
p1winList = []
p2winList = []
p1loseList = []
p2loseList = []
tieList = []
// ex.
// legalmovesList = [‘rock’, ‘scissors’, ‘paper’]
// nolegalmoves = 3
randstrat() {
return (legalmovesList[prand.n0(nolegalmoves)] );
}
reportbegin() {
gAction.setMessageParam('p1', p1);
gAction.setMessageParam('p2', p2);
if (p1 != gPlayerChar)
"<<'{The p1/he} and {the p2/he} decide to play a
quick game of ' + gaemName +'. \n'>>";
}
reportmoves() {
"{The p1/he} <<moveNameV>> <<p1move>>! ";
"{The p2/he} <<moveNameV>> <<p2move>>! ";
}
movekey () {
return (p1move + '-v-' + p2move);
}
resolveresult () {
local bothmoves = movekey();
outcome = '';
foreach (local thisPosRes in possibleResults) {
if ( thisPosRes.conditions.indexOf(bothmoves) )
{
//"\n<<bothmoves>> so therefore
//"<<thisPosRes.report>>"; // <--debugging
outcome += thisPosRes.report;
if (thisPosRes.p1) p1.lastGAEMresult = thisPosRes.res;
if (thisPosRes.p2) p2.lastGAEMresult = thisPosRes.res;
p1.lastGAEM = libGlobal.totalTurns;
p2.lastGAEM = libGlobal.totalTurns;
}
}
return (outcome);
}
getstrats () {
p1move = '';
p2move = '';
if (p1 != gPlayerChar)
p1move = randstrat();
else do
{
"You can enter... <<reportMovesLaundry()>>What is your choice? ";
p1move = toString(inputManager.getInputLine (nil, nil) );
//"You entered << '"' + p1move + '"' >>! \n";
}
while (legalmovesList.indexOf(p1move) == nil);
if (p2 != gPlayerChar)
p2move = randstrat();
else do
{
"You can enter... <<reportMovesLaundry()>>What is your choice? ";
p2move = toString(inputManager.getInputLine (nil, nil) );
//"You entered << '"' + p1move + '"' >>! \n";
}
while (legalmovesList.indexOf(p2move) == nil);
}
reportMovesLaundry ()
{
local accrete = '\n';
legalmovesList.forEach ({x: accrete += (' ' + x+ '\n') });
return accrete;
}
mainLoop() {
reportbegin();
getstrats();
reportmoves();
"<<resolveresult()>>";
}
fin () {
p1 = nil;
p2 = nil;
gaemName = nil;
}
;
//////// THE GENERAL RESULT OBJECT ///////////////////////////////////
Strat : object
gaem = ‘’
name = ‘’
opponent = ‘’
data = ‘’
;
Result : object
name = ‘’
report = ‘’
condits = [’’, ‘’]
// because = [’’, ‘’]
// sotherefore () {
// "modify and update players here; do not run inherited. ";
// }
p1 = nil
p2 = nil
res = 0
;
//////////////////////////////////////////////////////////////////////
//////// ROCK - PAPER - SCISSORS /////////////////////////////////////
//////////////////////////////////////////////////////////////////////
frpsGaem : GaemThing
//set up possible moves somehow…
gaemName = 'Rock-Paper-Scissors'
moveNameV = 'throw{s}'
legalmovesList = ['rock', 'scissors', 'paper']
nolegalmoves = 3
possibleResults =
[
p1winRPS ,
p2winRPS ,
p1loseRPS ,
p2loseRPS ,
p1drawRPS ,
p2drawRPS
]
;
//////// ROCK - PAPER - SCISSORS RESULTS /////////////////////////////
p1winRPS : Result
name = ‘p1win’
p1 = true
res = 1
report = '{The p1/he} win{s}! ’
conditions = [
‘rock-v-scissors’ ,
‘scissors-v-paper’ ,
‘paper-v-rock’
]
;
p1loseRPS : Result
name = ‘p1lose’
p1 = true
res = -1
report = '{The p1/he} lose{s}! ’
conditions = [
‘scissors-v-rock’ ,
‘paper-v-scissors’ ,
‘rock-v-paper’
]
;
p2winRPS : Result
name = ‘p2win’
p2 = true
res = 1
report = '{The p2/he} win{s}! ’
conditions = [
‘scissors-v-rock’ ,
‘paper-v-scissors’ ,
‘rock-v-paper’
]
;
p2loseRPS : Result
name = ‘p2lose’
p2 = true
res = -1
report = '{The p2/he} lose{s}! ’
conditions = [
‘rock-v-scissors’ ,
‘scissors-v-paper’ ,
‘paper-v-rock’
]
;
p1drawRPS : Result
name = ‘p1draw’
p1 = true
res = 0
report = '{The p1/he} ties! ’
conditions = [
‘rock-v-rock’ ,
‘paper-v-paper’ ,
‘scissors-v-scissors’
]
;
p2drawRPS : Result
name = ‘p2draw’
p2 = true
res = 0
report = '{The p2/he} ties! ’
conditions = [
‘rock-v-rock’,
‘paper-v-paper’,
‘scissors-v-scissors’
]
;
//////////////////////////////////////////////////////////////////////
/////// THE PRISONER’S DILEMMA ///////////////////////////////////////
//////////////////////////////////////////////////////////////////////
rpsGaem : GaemThing
//pdGaem : GaemThing
//set up possible moves somehow…
gaemName = 'Prisoner\'s Dilemma'
moveNameV = 'choose{s} to'
legalmovesList = ['stay loyal', 'defect']
nolegalmoves = 2
possibleResults =
[
p1winPD ,
p2winPD ,
p1losePD ,
p2losePD ,
p1loseBadlyPD ,
p2loseBadlyPD
]
;
/////// THE PRISONER’S DILEMMA RESULTS /////////////////////////////////////
p1winPD : Result
name = ‘p1win’
p1 = true
res = 1
report = '{The p1/he} win{s}! ’
conditions = [
‘stay loyal-v-stay loyal’
]
;
p1losePD : Result
name = ‘p1lose’
p1 = true
res = -1
report = '{The p1/he} lose{s}! ’
conditions = [
‘stay loyal-v-defect’ ,
‘defect-v-stay loyal’
]
;
p1loseBadlyPD : Result
name = ‘p1losebadly’
p1 = true
res = -10
report = '{The p1/he} lose{s} badly! ’
conditions = [
‘defect-v-defect’
]
;
p2winPD : Result
name = ‘p2win’
p2 = true
res = 1
report = '{The p2/he} win{s}! ’
conditions = [
‘stay loyal-v-stay loyal’
]
;
p2losePD : Result
name = ‘p2lose’
p2 = true
res = -1
report = '{The p2/he} lose{s}! ’
conditions = [
‘defect-v-stay loyal’ ,
‘stay loyal-v-defect’
]
;
p2loseBadlyPD : Result
name = ‘p2losebadly’
p2 = true
res = -10
report = '{The p2/he} lose{s} badly! ’
conditions = [
‘defect-v-defect’
]
;
//////////////////////////////////////////////////////////////////////
///////////// PLAY (RPS) HANDLING STUFF //////////////////////////////
//////////////////////////////////////////////////////////////////////
DefineTAction(RPS);
VerbRule(RPS)
‘play’ ‘rps’ ‘with’ singleDobj
: RPSAction
verbPhrase = ‘play/playing rps (with whom)’
;
modify Thing
dobjFor(RPS) {
verify() { illogical(’{The dobj/he} would be a pretty dull counterpart. '); }
}
;
modify Person
strat = [
]
lastGAEM = nil
lastGAEMresult = nil
// how often the person is willing to play RPS
GAEMfreq = 3
isHim = true
dobjFor(RPS) {
verify() {}
check() {
if (!wantsToPlay) {
local msg = '{You/he} want{s} to play RPS with
{the dobj/him}, but ';
if (lastGAEMresult == nil )
msg += '{the dobj/he} {is} busy. ';
else if (lastGAEMresult > 0 )
msg += '{the dobj/he} {is} still gloating over
{its/hers} win. ';
else if (lastGAEMresult < 0 )
msg += '{the dobj/he} {is} still smarting over
{its/hers} loss. ';
else if (lastGAEMresult == 0 )
msg += '{the dobj/he} {is} still thinking over
{its/hers} tactics. ';
failCheck(msg);
}
}
action() {
rpsGaem.p1 = gActor;
rpsGaem.p2 = self;
rpsGaem.mainLoop;
}
}
wantsToPlay()
{
local ans = ‘’;
if ( self != gPlayerChar)
return (GAEMfreq && (lastGAEM == nil ||
(lastGAEM <= libGlobal.totalTurns - GAEMfreq)));
else do
{
"{The actor/he} asks you to play a game. ";
"Do you want to (y/n)? ";
ans = toString(inputManager.getInputLine (nil, nil) );
ans.toLower();
}
while ([‘y’, ‘yes’, ‘n’, ‘no’].indexOf(ans) == nil);
return ([‘y’, ‘yes’].indexOf(ans) != nil);
}
;
//////////////////////////////////////////////////////////////////////
class RPSAgenda: AgendaItem
initiallyActive = true
isReady() {
return getActor.wantsToPlay && prand.n0(3) == 2; //pseudo-randomize these
}
invokeItem() {
local npcs = getActor.sensePresenceList(sight).subset({obj:
obj.ofKind(Actor) && obj != getActor //&& obj != gPlayerChar
});
// “<<(npcs.sublist(1)).valWhich({x: x==x}) >>”;
local pdn = prand.n0( npcs.length() ); //?
// "PDN = <>. ";
if (npcs.length)
newActorAction(getActor, RPS, npcs[pdn] ); //rand(npcs)); //pseudo-randomize this
}
;
//////////////////////////////////////////////////////////////////////
guard: Person ‘guard’ ‘guard’ @lab
"Just another guard. Although doubtless with his own inner life when
he’s not playing an NPC in a TADS game. "
GAEMfreq = 5
;
scientist: Person ‘scientist’ ‘scientist’ @lab
“Just another scientist. But with his own human dramas and foibles
when he goes home to his family, no doubt.”
GAEMfreq = 0
;
janitor: Person ‘janitor’ ‘janitor’ @lab
"Just another janitor. Probably union. "
;
//////////////////////////////////////////////////////////////////////
[/code][/rant]
Many thanks, Ben, for all the help with this; and the project continues.
Conrad.