The Third Dynasty of pokes: helper code

From BlogNomic Wiki
Jump to navigation Jump to search

The schema of a sqlite database with gamestate information:

CREATE TABLE player (
       name text primary key,
       slush integer,
       active integer,
       integrity integer,
       money integer
);
CREATE TABLE client (
       name text primary key,
       shortname text unique,
       attorney text,
       villainy integer,
       worth integer,
       type text
);
CREATE TABLE cases (
       id integer,
       open integer,
       plaintiff text,
       defendant text,
       accusation text,
       p_guilt integer,
       d_guilt integer,
       foreign key(plaintiff) references client(name),
       foreign key(defendant) references client(name)
);
CREATE TABLE representation (
       client text,
       player text, active,
       foreign key(client) references client(name),
       foreign key(player) references player(name)
);
CREATE VIEW active_representation as select client, player from representation inner join player
  where player.name = representation.player and player.active = 1 and representation.active = 1;

The final version of pokes-iii.lisp. "If either Party of the Case is represented by an Attorney who is already representing a Party in another Open Case, skip the rest of this atomic action" is not implemented; I did this step by hand.

(defpackage #:pokes-iii
  (:use #:cl))

(in-package #:pokes-iii)

(defvar *crimes*
  '("Arson" "Burglary" "Murder" "Perjury" "Jaywalking"))
(defvar *complaints*)
(setf *complaints*
      '("Personal Injury" "Defamation" "Negligence" "Malpractice" "Probate" "Unpaid Debt"))

(defun prompt-gndt (options)
  "Pick a random option from the GNDT. Substitutable for SECRETLY-RANDOM."
  (loop :for opt :in options :do
     (progn (princ opt) (princ " ")))
  (princ "DICE") (princ (length options))
  (terpri)
  (princ ">>> ")
  (- (read) 1))

(defun secretly-random (options)
  "Pick a random option secretly. Substitutable for PROMPT-GNDT."
  (format t "[random of: ~a] " options)
  (let* ((n (length options))
         (c (random n)))
    (format t "~a/~a~%" c n)
    c))
  ;;(random (length options)))

(defun is-business-p (db name)
  "Check if NAME is a business, for the purposes of transforming complaint to unpaid debt."
  (< 0 (sqlite:execute-single
        db "select count(*) from client where name=? and type='business';" name)))

(defun verify-integrity (db)
  "Find player/client pairs that have current representation and the player is mistrusted."
  (let ((reps (sqlite:execute-to-list
               db "select p.name, c.name, p.integrity, c.villainy from player as p inner join active_representation as r inner join client as c where r.player = p.name and r.client = c.name;"))
        (ok t))
    (loop :for r :in reps :do
       (when (> 5 (+ (third r) (fourth r)))
         (setf ok nil)
         (format t "*** ~a mistrusts ~a~%" (second r) (first r))))
    (if ok (format t "everything is okay~%"))))

(defun set-client-worth-by-name (db name worth)
  (format t "*** update the worth of ~a: ~a~%" name worth)
  (sqlite:execute-non-query
   db "update client set worth=? where name=?;" worth name))

(defun get-client-worth-by-name (db name)
  (sqlite:execute-single db "select worth from client where name=?;" name))

(defun increase-client-worth-by-name (db name inc)
  (let* ((worth (get-client-worth-by-name db name))
         (new-worth (+ worth inc)))
    (format t "*** increasing the worth of ~a: ~a -> ~a~%" name worth new-worth)
    (sqlite:execute-non-query
     db "update client set worth=? where name=?;" new-worth name)))

(defun increase-player-money-loudly (db player inc)
  (let* ((money (sqlite:execute-single db "select money from player where name=?;" player))
         (new-money (max 0 (+ money inc))))
    (format t "*** increase the public money of ~a: ~a -> ~a~%" player money new-money)
    (sqlite:execute-non-query
     db "update player set money=? where name=?;" new-money player)))

(defun increase-player-money-quietly (db player inc)
  (let* ((slush (sqlite:execute-single db "select slush from player where name=?;" player))
         (new-slush (+ slush inc)))
    (format t "*** notify ~a of slush fund increase: ~a -> ~a~%" player slush new-slush)
    (sqlite:execute-non-query
     db "update player set slush=? where name=?;" new-slush player)))

(defun attorneys-of (db client)
  "Get the (string) names of all players representing CLIENT."
  (mapcar #'first
          (sqlite:execute-to-list
           db "select player from active_representation where client=?;" client)))

(defun is-represented-p (db client)
  "Test if CLIENT has any representation."
  (if (= 0 (length (attorneys-of db client))) nil t))

(defun is-represented-by-p (db client player)
  "Test if CLIENT is represented by PLAYER."
  (if (= 0 (sqlite:execute-single
            db "select count(*) from active_representation where player=? and client=?;"
            player client))
      nil t))

(defun decrease-player-integrity-loudly (db player decrement)
  (let* ((integrity (sqlite:execute-single db "select integrity from player where name=?;" player))
         (new-integrity (max 0 (- integrity decrement))))
    (format t "*** integrity of player ~a decreases: ~a -> ~a~%" player integrity new-integrity)
    (sqlite:execute-non-query db "update player set integrity=? where name=?;" new-integrity player)))

(defun adjust-guilt-by-votes (db guilt party other-party
                              votes-for votes-against votes-veto)
  (let ((adj-guilt guilt))
    (loop :for vote :in votes-for :do
       (when (is-represented-by-p db party vote)
         (format t "adjusting guilt of ~a via FOR vote from ~a: ~a -> ~a~%" party vote
                 adj-guilt (- adj-guilt 2))
         (increase-player-money-loudly db vote -2000)
         (decf adj-guilt 2)))
    (setf adj-guilt (max 0 adj-guilt))
    (loop :for vote :in votes-against :do
       (when (is-represented-by-p db other-party vote)
         (format t "adjusting guilt of ~a via AGAINST vote from ~a: ~a -> ~a~%" party vote
                 adj-guilt (+ adj-guilt 1))
         (decrease-player-integrity-loudly db vote 1)
         (incf adj-guilt 1)))
    (loop :for vote :in votes-veto :do
       (when (is-represented-by-p db party vote)
         (format t "adjusting guilt of ~a via VETO vote from ~a: ~a -> ~a~%" party vote
                 adj-guilt (+ adj-guilt 10))
         (decrease-player-integrity-loudly db vote 2)
         (incf adj-guilt 10)))
    adj-guilt))

(defun check-spelling (db player)
  (if (= 0 (sqlite:execute-single db "select count(*) from player where name=?;" player))
      (error "player ~a wasn't found in the database" player)))

(defun check-all-spellings (db players)
  (loop :for p :in players :do (check-spelling db p)))

(defun generate-case (db)
  (sqlite:with-transaction db
    (when (< 2 (sqlite:execute-single
                db "select count(*) from cases where open=1;"))
      (format t "aborting: 2 open cases already~%")
      (return-from generate-case nil))
    (let* ((clients
            (sqlite:execute-to-list
             db "select name, shortname, villainy, type from client;"))
           ;;(nclients (length clients))
           (plaintiff-id
            (secretly-random (mapcar #'second clients))) ;; formerly prompt-gndt
           (plaintiff (nth plaintiff-id clients))
           (defendants
            (remove-if (lambda (c) (or (eq c plaintiff)
                                       (equal (fourth c) "state"))) clients))
           (defendant-id
            (secretly-random (mapcar #'second defendants)))
           (defendant (nth defendant-id defendants)))
      (format t "preliminary: ~a v ~a~%" (first plaintiff) (first defendant))
      (when (or (< 0 (length (intersection (attorneys-of db (first plaintiff))
                                           (attorneys-of db (first defendant))
                                           :test #'equal)))
                (not (is-represented-p db (first plaintiff)))
                (not (is-represented-p db (first defendant))))
        (format t "aborting: no or bad representation~%")
        (return-from generate-case nil))
      (let* ((complaint
              (if (equal (fourth plaintiff) "state")
                  (nth (prompt-gndt *crimes*) *crimes*)
                  (nth (prompt-gndt *complaints*) *complaints*)))
             (plaintiff-guilt (random (1+ (* 2 (third plaintiff)))))
             (defendant-guilt (random (1+ (* 2 (third defendant)))))
             (case-id (progn (princ "case id>>> ") (read))))
        (if (and (equal complaint "Probate")
                 (or (is-business-p db (first plaintiff)) (is-business-p db (first defendant))))
            (setf complaint "Unpaid Debt"))
        (if (= plaintiff-guilt defendant-guilt)
            (if (= (random 2) 0) (incf plaintiff-guilt) (incf defendant-guilt)))
        (format t "... plaintiff: ~a (guilt ~a) defendant: ~a (guilt ~a) complaint: ~a~%"
                (first plaintiff) plaintiff-guilt
                (first defendant) defendant-guilt
                complaint)
        (sqlite:execute-non-query
         db "insert into cases values (?, 1, ?, ?, ?, ?, ?);"
         case-id (first plaintiff) (first defendant) complaint
         plaintiff-guilt defendant-guilt)
        (loop :for rep :in (attorneys-of db (first plaintiff))
           :do (format t "*** notify attorney ~a of plaintiff ~a guilt ~a~%"
                       rep (first plaintiff) plaintiff-guilt))
        (loop :for rep :in (attorneys-of db (first defendant))
           :do (format t "*** notify attorney ~a of defendant ~a guilt ~a~%"
                       rep (first defendant) defendant-guilt))
        (list plaintiff defendant plaintiff-guilt defendant-guilt complaint)))))

(defun resolve-case (db case-id votes-for votes-against votes-veto)
  (sqlite:with-transaction db
    (check-all-spellings db votes-for)
    (check-all-spellings db votes-against)
    (check-all-spellings db votes-veto)
    (multiple-value-bind
          (plaintiff-name defendant-name p-guilt d-guilt)
        (sqlite:execute-one-row-m-v
         db "select plaintiff, defendant, p_guilt, d_guilt from cases where id=?" case-id)
      (setf p-guilt (adjust-guilt-by-votes db p-guilt plaintiff-name defendant-name
                                           votes-for votes-against votes-veto))
      (setf d-guilt (adjust-guilt-by-votes db d-guilt defendant-name plaintiff-name
                                           votes-for votes-against votes-veto))
      (let* ((plaintiff-won (< p-guilt d-guilt))
             (defendant-worth (get-client-worth-by-name db defendant-name))
             (winner-name (if plaintiff-won plaintiff-name defendant-name))
             (loser-name (if plaintiff-won defendant-name plaintiff-name))
             (winner-attorneys
              (mapcar #'first
                      (sqlite:execute-to-list
                       db "select player from active_representation where client=?;"
                       winner-name)))
             (fee (if plaintiff-won (floor (* defendant-worth 0.3)) 0)))
        (format t "the fine is: ~a~%" fee)
        (increase-client-worth-by-name db winner-name (floor (* fee 0.9)))
        (increase-client-worth-by-name db loser-name (- fee))
        (loop :for player :in winner-attorneys :do
           (let ((repped (< 0 (count-if (lambda (v) (equal v player)) votes-for))))
             (if repped
                 (increase-player-money-loudly db player (floor (* fee 0.1)))
                 (increase-player-money-quietly db player (floor (* fee 0.1))))))
        (sqlite:execute-non-query db "update cases set open=0 where id=?;" case-id)))))