#!/pkg/gnu/bin/gawk -f BEGIN { # # this script regulates the play of ronald loui's and anne jump's game # which is intended to model the following aspects of negotiation # which are absent in game-theoretic approaches: # # re: search # the creation of focal points through dialogue and search # interplay of individual and social search # joint problem-solving through resource exchange and search # cost of search and heuristic/satisficing search # multidimensionality of settlement options generated, not predefined # # re: language # explicit threats and queries # unrestricted language # # re: purpose # multiplicity of winning conditions # # re: method # importance of design not analysis # importance of process not outcome # importance of player input to a process, not equilibria # # this game was developed during the summer of 1997 by ronald p. loui # and anne h. jump, a consultant, funded by the national science foundation # program on information technology and organizations within the # information, robotics, and intelligent systems division of the # computer and information systems engineering directorate. # version 4.3 of 1/98 rpl # 1.0 attempts as clean code as possible for basic functionality # 2.0 MAJOR REVISION: permits programs to be used to generate commands # 2.1 can pipe output to /dev/stdout and file for programs to read # 2.2 implements otake # 2.3 puts limit on how long to negotiate # 2.4 implements the verbose option # 2.5 fixes a bug that lets you accept your own proposals # 2.6 contains all the code needed to run as a tournament # 3.0 of 12/97 permits gui monitoring of negotiation # 4.0 MAJOR REVISION sends output to the web # 4.1 MAJOR REVISION reads, processes a single command, then saves # 4.2 cleaner HTML table, input mostly separate from payoff presentation # 4.3 MAJOR REVISION abstracts players' names (anne --> playerA) # 5.0 MAJOR REVISION to rules of the game: pilecardmin verbose = 1 gui = 0 webout = 1 cgidebug = 1 print "
"

  playerAprog = ARGV[1]
  playerBprog = ARGV[2]
  pairing = ARGV[3]

  # each program takes a view.playerB or view.playerA and generates a move
  # assume these programs are executable, and paired with X.howhappy, which is also executable

  if (playerAprog ~ "---") playerAprog = ""
  if (playerBprog ~ "---") playerBprog = ""

  if (playerAprog) { playerAcom = playerAprog " playerA" }
  if (playerBprog) { playerBcom = playerBprog " playerB" }

  # this may need to be set differently for tournament or cgi
  PATH = "/home/cs/faculty/loui/.www-docs/313f97/"
  PATH = "./"
  savefile = PATH "anne.save"
  dialoguefile = PATH "anne.dialogue"

  srand()

  if (restore()) {
    print "restoring"
  } else {
    print "reinitializing"
    if (rand() < .5) whoseturn = "playerA"
    else whoseturn = "playerB"
    init()
    deal()
    revealsomecards()
    save()
    if (webout) viewweb()
    # don't process input on a reinitialization
    exit
  }

  if (negotiate(1)) {
    revealremaining()
    if (webout) finalweb()
    if (verbose) view("playerA and playerB","/dev/stdout")
    traderesources()
    doexchanges()
    if (verbose) view("playerA and playerB","/dev/stdout")
    sumcells()
    reportsums()
    reportvalue(finalrow,finalcol)
    askhowhappy()
  
    score()
    makesocialrecord()

  } else save()

  if (cgidebug) print "CGIDEBUG normal exit"

}

function finalweb( i) {
  prhtml("A Better Negotiation Game, designed by R.P. Loui and A.H. Jump, Summer 1997.")
  prhtml("


") prhtml("

FINAL OUTCOME

") prhtml("EXPLANATION") prhtml("
") timestampthisform() prhtml("") prhtml("
") viewwebhow("playerA",1) viewwebhow("playerB",1) # don't close because values will be reported # erase the savefile close(savefile) print "" > savefile close(savefile) } function save( i,j,ntemp) { # rows, cols # playerA, playerB # annresources # playerBresources # count: # resources # count: # proposals # count: # sidepayments # count: # sidetakings # whoseturn # forcingperson # finalrow # finalcol # plies # search stats print rows, cols > savefile for (i=1; i<=rows; i++) for (j=1; j<=cols; j++) print playerA[i,j], playerB[i,j] > savefile print playerAresources > savefile print playerBresources > savefile ntemp = 0 for (i in resources) if (resources[i]) ntemp++ print ntemp, "resources" > savefile for (i in resources) if (resources[i]) print i,resources[i] > savefile ntemp = 0 for (i in activeprop) if (activeprop[i]) ntemp++ print ntemp, "activeprop" > savefile for (i in activeprop) if (activeprop[i]) print i,activeprop[i] > savefile ntemp = 0 for (i in sidepay) ntemp++ print ntemp, "sidepay" > savefile for (i in sidepay) print i,sidepay[i] > savefile ntemp = 0 for (i in sidetake) ntemp++ print ntemp, "sidetake" > savefile for (i in sidetake) print i,sidetake[i] > savefile print whoseturn, "whoseturn" > savefile print forcingperson > savefile print finalrow > savefile print finalcol > savefile print plies > savefile print maxplies > savefile print playerAfree > savefile print playerAfreeused > savefile print playerAcost > savefile print playerAcostused > savefile print playerBfree > savefile print playerBfreeused > savefile print playerBcost > savefile print playerBcostused > savefile close(savefile) } function restore( i,j,ntemp) { if ((getline < savefile) == -1) return 0 # actually, need an empty file to save write-permissions if (!$0) return 0 rows = $1; cols = $2 for (i=1; i<=rows; i++) for (j=1; j<=cols; j++) { getline < savefile playerA[i,j] = $1; playerB[i,j] = $2 } getline playerAresources < savefile getline playerBresources < savefile getline < savefile ntemp = $1 for (i=1; i<=ntemp; i++) { getline < savefile resources[$1] = $2 } getline < savefile ntemp = $1 for (i=1; i<=ntemp; i++) { getline < savefile activeprop[$1] = $2 } getline < savefile ntemp = $1 for (i=1; i<=ntemp; i++) { getline < savefile sidepay[$1] = $2 } getline < savefile ntemp = $1 for (i=1; i<=ntemp; i++) { getline < savefile sidetake[$1] = $2 } getline < savefile whoseturn = $1 getline forcingperson < savefile getline finalrow < savefile getline finalcol < savefile getline plies < savefile getline maxplies < savefile getline playerAfree < savefile getline playerAfreeused < savefile getline playerAcost < savefile getline playerAcostused < savefile getline playerBfree < savefile getline playerBfreeused < savefile getline playerBcost < savefile getline playerBcostused < savefile close(savefile) return 1 } function report(x) { print x violation = x } function negotiate(oneturn) { # the oneturn flag restricts this to one input # returns 1 if agreement/breakdown # if (rand() < .5) whoseturn = "playerA" # else whoseturn = "playerB" do { plies++ if (plies > maxplies) { print "negotiation too long... exiting!", plies exit } if (verbose) { print "\t\t\t\t_______________" print "\n\t\t\t\t "whoseturn"'s turn" print "\t\t\t\t_______________" view(whoseturn,"/dev/stdout") } if (cgidebug) print "CGIDEBUG beginning program views" if (playerAprog || playerBprog) view(whoseturn,outfile(whoseturn)) # if (verbose) if (++turn <= 2) printmenu() if (gui) viewgui() if (forcingperson == whoseturn) { print whoseturn" has left the negotiation with " print lastforce $0 = lastforce savecommand = $0 } else if (playerAprog && whoseturn ~ /playerA/) { playerAcom | getline savecommand = $0 close(playerAcom) # protect from spies # system("rm -f "outfile(whoseturn)) violation = "" command() } else if (playerBprog && whoseturn ~ /playerB/) { playerBcom | getline savecommand = $0 close(playerBcom) # protect from spies # system("rm -f "outfile(whoseturn)) violation = "" command() } else { printf "\t\t\t"whoseturn"'s action (or 'menu' or 'redraw')?"; getline savecommand = $0 if ($0 ~ /menu/) { printmenu() printf "\t\t\t\t"whoseturn"'s action (or 'redraw')?"; getline } if ($0 ~ /redraw/) { continue } else { resultingview = 0; # could be set to 1 in command violation = "" command() } } if (cgidebug) print "CGIDEBUG updating dialogue" addtodialogue(savecommand) if (cgidebug) print "CGIDEBUG updated dialogue" if (verbose) if (resultingview) view(whoseturn,"/dev/stdout") if (whoseturn == "playerA") whoseturn = "playerB" else whoseturn = "playerA" if (forcingperson) whoseturn = otherplayer(forcingperson) if (cgidebug) print "CGIDEBUG updating webout" if (webout) viewweb() if (cgidebug) print "CGIDEBUG updated webout" } while ((!agreement && !oneturn) || savecommand ~ /redraw/) if (cgidebug) print "CGIDEBUG normal return from negotiate" return agreement } function outfile(x) { if (x ~ /playerB/) return PATH "view.playerB" if (x ~ /playerA/) return PATH "view.playerA" } function command(new,n,cards,i) { if (cgidebug) print "\nCGIDEBUG entering command processing of "$0 if (playerAprog && whoseturn ~ /playerA/) printf playerAprog" " if (playerBprog && whoseturn ~ /playerB/) printf playerBprog" " print whoseturn": "$0 # only response to a force is a force if (forcingperson) { if ($1 !~ /fo/) report( "PROTOCOL VIOLATION: only response to force is force" ) } # accept if ($1 ~ /^ac/) { if (activeprop[$2,$3,otherplayer(whoseturn)]) { finalrow = $2 finalcol = $3 agreement = 1 } else report( "PROTOCOL VIOLATION: accept only proposed offers" ) } # propose if ($1 ~ /^pr/) if (kosher($2,$3)) { activeprop[$2,$3,whoseturn] = 1 specialmarks[$2,$3] = "***" } # rescind proposal, unpropose if ($1 ~ /^unpr/) if (kosher($2,$3)) { if (!activeprop[$2,$3,whoseturn]) report( "PROTOCOL VIOLATION: propose before you rescind" ) else delete activeprop[$2,$3,whoseturn] for (i in sidepay) split(i,temp,"\034") if (temp[1] == $2 && temp[2] == $3 && temp[4] == whoseturn) { delete sidepay[i] print "deleting sidepayment "i" with the proposal." } for (i in sidetake) split(i,temp,"\034") if (temp[1] == $2 && temp[2] == $3 && temp[4] == whoseturn) { delete sidetake[i] print "deleting sidetaking "i" with the proposal." } } # search if ($1 ~ /^se/) if (kosher($2,$3)) { if (!chargeto(whoseturn)) return new = getcard() if (whoseturn == "playerA") { psrc[$2,$3] = psrc[$2,$3] "a" specialmarks[$2,$3] = "+++" n = sub(/\?/,new,playerA[$2,$3]) } if (whoseturn == "playerB") { psrc[$2,$3] = psrc[$2,$3] "b" specialmarks[$2,$3] = "+++" n = sub(/\?/,new,playerB[$2,$3]) } if (!n) report( "PROTOCOL VIOLATION: no more cards to reveal" ) resultingview = 1 } # generate substitution if ($1 ~ /^su/) if (kosher($2,$3)) { if (!chargeto(whoseturn)) continue n = $4 if (whoseturn == "playerA") cards = playerA[$2,$3] if (whoseturn == "playerB") cards = playerB[$2,$3] parsetotemp(cards) if (temp[n] ~ /[rb]/) { new = getcard() temp[n] = temp[n]">"new } else report( "PROTOCOL VIOLATION: no such card to substitute" ) cards = "" for (i=1; i<=nt; i++) { cards = cards temp[i] } if (whoseturn == "playerA") playerA[$2,$3] = cards if (whoseturn == "playerB") playerB[$2,$3] = cards resultingview = 1 } # threaten row or col for playerA or playerB if ($1 ~ /^fo/) { if (whoseturn == "playerA") { threat = $2 if ($3 != ".") { report( "PROTOCOL VIOLATION: use . for column" ) return } if (!kosher(threat,1)) { report( "PROTOCOL VIOLATION: no such row" ) return } } if (whoseturn == "playerB") { threat = $3 if ($2 != ".") { report( "PROTOCOL VIOLATION: use . for row" ) return } if (!kosher(1,threat)) { report( "PROTOCOL VIOLATION: no such column" ) return } } } # ask what's wplayerBg with a proposal, whynot if ($1 ~ /^wh/) { if (!activeprop[$2,$3,whoseturn]) { report( "PROTOCOL VIOLATION: propose before you ask" ) return } } # ask if other has a card, haveyou if ($1 ~ /^ha/) { if (!iscard($2)) { report( "PROTOCOL VIOLATION: resource must be a card" ) return } } # offer a card as sidepayment to a proposal if ($1 ~ /^of/) { if (!(resources[whoseturn,$4])) { report( "PROTOCOL VIOLATION: offer only what you have" ) return } sidepay[$2,$3,$4,whoseturn] = 1 } # offer to take a card as sidepayment to a proposal if ($1 ~ /^ot/) { sidetake[$2,$3,$4,whoseturn] = 1 } # retract an offer a card as sidepayment to a proposal, unoffer if ($1 ~ /^unof/) { if (!sidepay[$2,$3,$4,whoseturn] && !sidepay[$2,$3,$4,whoseturn]) { report( "PROTOCOL VIOLATION: no such sidepayment offered" ) return } if (!activeprop[$2,$3,whoseturn]) { report( "PROTOCOL VIOLATION: no such proposal active") return } delete sidepay[$2,$3,$4,whoseturn] delete sidetake[$2,$3,$4,whoseturn] } # warn that breakdown is imminent if ($1 ~ /^wa/) { # nothing to do here } # force breakdown for playerA or playerB if ($1 ~ /^fo/) { if (whoseturn == "playerA") { threat = $2 if ($3 != ".") { report( "PROTOCOL VIOLATION: use . for column" ) return } if (!kosher(threat,1)) { report( "PROTOCOL VIOLATION: no such row" ) return } finalrow = $2 } if (whoseturn == "playerB") { threat = $3 if ($2 != ".") { report( "PROTOCOL VIOLATION: use . for row" ) return } if (!kosher(1,threat)) { report( "PROTOCOL VIOLATION: no such column" ) return } finalcol = $3 } if (forcingperson) { # this is the response to a force agreement = 1 } forcingperson = whoseturn lastforce = $0 } if (cgidebug) print "CGIDEBUG normal return from command function" } function iscard(x) { if (x ~ /^[rb][0-9]$/) return 1 if (x ~ /^[rb]10$/) return 1 if (x ~ /^[rb]11$/) return 1 if (x ~ /^[rb]12$/) return 1 if (x ~ /^[rb]13$/) return 1 return 0 } function otherplayer(x) { if (x == "playerA") return "playerB" if (x == "playerB") return "playerA" } function addtodialogue(x) { print whoseturn,x,violation >> dialoguefile if (cgidebug) print "CGIDEBUG closing dialogue file" close(dialoguefile) if (cgidebug) print "CGIDEBUG dialogue file closed" system("chmod ogu+r " dialoguefile) } function revealsomecards(n,maxreveal) { maxreveal = int(10*rand()) for (n=1; n<=maxreveal; n++) { revealplayerA(int(rand()*rows)+1, int(rand()*cols)+1) revealplayerB(int(rand()*rows)+1, int(rand()*cols)+1) } } function revealplayerA(i,j) { sub(/\?/,getcard(),playerA[i,j]) } function revealplayerB(i,j) { sub(/\?/,getcard(),playerB[i,j]) } function chargeto(x) { if (x ~ /playerA/) { if (playerAfreeused < playerAfree) { playerAfreeused++; return 1 } if (playerAcostused < playerAcost) { playerAcostused++; return 1 } report( "PROTOCOL VIOLATION: sorry, no search left" ) return 0 } if (x ~ /playerB/) { if (playerBfreeused < playerBfree) { playerBfreeused++; return 1 } if (playerBcostused < playerBcost) { playerBcostused++; return 1 } report( "PROTOCOL VIOLATION: sorry, no search left" ) return 0 } } function kosher(x,y,res) { res = 1 if (x != int(x) || y != int(y)) res = 0 if (x < 1 || y < 1) res = 0 if (x > rows || y > cols) res = 0 if (!res) report( "PROTOCOL VIOLATION: no such row and column" ) return res } function printmenu() { print "accept x y accept the proposal " print "prop x y make (or repeat) a proposal" print "unprop x y rescind a proposal" print "search x y reveal more about a payoff" print "sub x y n generate substitution for the nth card of a payoff" print "whynot x y ask why not a proposal" print "threaten . y threaten breakdown with column y (playerB only)" print "threaten x . threaten breakdown with row x (playerA only)" print "warnbreak warn that breakdown is imminent" print "force . y unilaterally breakdown (playerB only)" print "force x . unilaterally breakdown (playerA only)" print "haveyou c ask for the resource, c" print "idohave c confirm having c" print "idonthave c deny having c" print "offer x y c offer a resource, conditional on agreeing to " print "otake x y c offer to take a resource, conditional on " print "unoffer x y c retract an offered resource" } function init() { rows = int(6*rand())+3 cols = int(3*rand())+3 maxplies = int(500*rand()+20.5) system("clear") print "\t\t\tA BETTER NEGOTIATION GAME" print "" print "" print "" if (playerAprog) { print "program for playerA is "playerAprog } if (playerBprog) { print "program for playerB is "playerBprog } print "rows " rows " (playerA controls under breakdown)" print "cols " cols " (playerB controls under breakdown)" playerAfree = int(10*rand())+1 playerAcost = int(10*rand())+1 playerAcount = int(20*rand())+1 playerAfreeused = 0; playerAcostused = 0 playerBfree = int(10*rand())+1 playerBcost = int(10*rand())+1 playerBcount = int(10*rand())+1 playerBfreeused = 0; playerBcostused = 0 # system("rm -f anne.dialogue") # rewrite file from top, thus saving read/write permissions print playerAprog " AND " playerBprog" ROUND "pairing > dialoguefile print "rows",rows,"cols",cols,"a new game!" > dialoguefile close(dialoguefile) } function deal(i,j,new) { basic = "???" for (i=1; i<=rows; i++) { for (j=1; j<=cols; j++) { playerA[i,j] = basic playerB[i,j] = basic } } augment(1,1) augment(2,2) augment(2,3) augment(3,3) augment(4,1) playerAresources = " " for (i=1; i<=playerAcount; i++) { new = getcard() if (!index(playerAresources," "new" ")) playerAresources = playerAresources new " " resources["playerA",new] = 1 } gsub("^ ","",playerAresources) gsub(" $","",playerAresources) playerBresources = " " for (i=1; i<=playerBcount; i++) { new = getcard() if (!index(playerBresources," "new" ")) playerBresources = playerBresources new " " resources["playerB",new] = 1 } gsub("^ ","",playerBresources) gsub(" $","",playerBresources) } function getcard(rank,color) { if (rand() < .5) color = "r" else color = "b" rank = int(rand()*13)+1 return color rank } function augment(x,y, i,j) { for (i=1; i<=x; i++) { for (j=1; j<=y; j++) { playerA[i,j] = playerA[i,j] "?" playerB[rowrev(i),colrev(j)] = playerB[rowrev(i),colrev(j)] "?" } } } function rowrev(i) { return rows-i+1 } function colrev(i) { return cols-i+1 } function makesearch(x,y, i,result) { result = "" for (i=1; i<=x; i++) result = result "0" for (i=1; i<=y; i++) result = result "1" return result } function prhtml(x) { print x > PATH "test.html" } function viewweb() { if (whoseturn ~ /A/) prhtml("") else prhtml("") prhtml("EXPLANATION") viewwebhow(whoseturn,0) prhtml("


") prhtml("A Better Negotiation Game, designed by R.P. Loui and A.H. Jump, Summer 1997.") close(PATH "test.html") } function viewwebhow(whose,finalview, i,j,k,cards) { prhtml("
") prhtml("
") prhtml("

"whose"'s view
") prhtml("search
") dosearch(whose) prhtml("
") prhtml("resources
") doresources(whose) prhtml("

") prhtml("") prhtml("") docolheaders(whose) for (i=1; i<=rows; i++) { dorowheader(whose,i) for (j=1; j<=cols; j++) { if (whose == "playerA") cards = playerA[i,j] if (whose == "playerB") cards = playerB[i,j] for (k in temp) delete temp[k] parsetotemp(cards) docell(whose,i,j,finalview) } prhtml("") # terminates the ith row } prhtml("
") if (!finalview) { if (!forcingperson) dolegend(whose) else doforcelegend(whose) # name the form with the player getting the view prhtml("
") prhtml("") timestampthisform() } prhtml("
") if (!finalview) dolastmove(otherplayer(whose)) prhtml("
") } function timestampthisform() { "date" | getline date close("date") prhtml("
") } function dosearch(whose) { if (whose == "playerA") { prhtml("") prhtml(makesearch(playerAfreeused,playerAcostused)) prhtml("") prhtml(" ") prhtml(makesearch(playerAfree-playerAfreeused,playerAcost-playerAcostused)) } if (whose == "playerB") { prhtml("") prhtml(makesearch(playerBfreeused,playerBcostused)) prhtml("") prhtml(" ") prhtml(makesearch(playerBfree-playerBfreeused,playerBcost-playerBcostused)) } } function doresources(whose, temp,i) { prhtml("") if (whose == "playerA") split(playerAresources,temp," ") if (whose == "playerB") split(playerBresources,temp," ") for (i=1; i in temp; i++) { if (substr(temp[i],1,1) == "b") prhtml("") } prhtml("

") else prhtml("

") prhtml(substr(temp[i],2)) #prhtml("
") #textbutton("offer w/",temp[i]) #prhtml("
") #prhtml("") #prhtml("
") #prhtml("") #prhtml("") prhtml("

") } function makerowoptions( i,j) { prhtml("