Skip to content

Instantly share code, notes, and snippets.

@jpfuentes2
Last active December 27, 2015 04:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jpfuentes2/7269002 to your computer and use it in GitHub Desktop.
Save jpfuentes2/7269002 to your computer and use it in GitHub Desktop.
(ns labrepl.ipd)
;; moves and utils
(defn defect [moves] (conj moves 0))
(defn coop [moves] (conj moves 1))
(defn defect? [move] (= move 0))
(defn coop? [move] (= move 1))
(defn random [mover]
(conj mover (rand-int 2)))
(defn if-empty [mover strategy-when-empty otherwise]
(if (empty? mover)
(strategy-when-empty mover)
(otherwise mover)))
(defn any-defects? [moves]
(contains? (set moves) 0))
(defn count-choice [choice moves]
(reduce (fn [sum move]
(if (choice move)
(inc sum)
sum)) 0 moves))
(defn num-defects [mover]
(count-choice defect? mover))
(defn num-coops [mover]
(count-choice coop? mover))
;; end moves and utils
;; strategies
(defn AlwaysDefect [mover opponent] (defect mover))
(defn AlwaysCooperate [mover opponent] (coop mover))
(defn AlwaysRandom [mover opponent] (random mover))
(defn TitForTat [mover opponent]
(if-empty mover coop #(conj % (last opponent))))
(defn TitForTwoTats [mover opponent]
(if (<= (count mover) 1)
(coop mover)
(if (every? defect? (take-last 2 opponent))
(defect mover)
(coop mover))))
(defn SuspiciousTitForTat [mover opponent]
(if-empty mover defect #(TitForTat % opponent)))
(defn Grudger [mover opponent]
(if-empty mover coop (fn [mover]
(if (some defect? opponent)
(defect mover)
(coop mover)))))
;; Soft Grudger - Co-operates until the opponent defects, in such case opponent
;; is punished with d,d,d,d,c,c.
(defn SoftGrudger [mover opponent]
(if-not (any-defects? opponent)
(coop mover)
(if (= [(last mover) (last opponent)] [1 1])
(coop mover)
(let [last-6 (take-last 6 mover)]
(if-not (= (take-last 4 last-6) [0 0 0 0])
(defect mover)
(coop mover))))))
;; Gradual - Co-operates until the opponent defects, in such case defects the total
;; number of times the opponent has defected during the game. Followed up by two co-operations.
(defn Gradual [mover opponent]
(if-not (any-defects? opponent)
(coop mover)
(if (= (num-defects mover) (num-defects opponent))
(coop mover)
(defect mover))))
;; Naive Prober (Tit For Tat with Random Defection) - Repeat opponent's last choice (ie Tit For Tat),
;; but sometimes probe by defecting in lieu of co-operating.*
(defn NaiveProber [mover opponent]
(let [mover (TitForTat mover opponent) n (count mover)]
(if (and (coop? (last mover)) (> n 1))
(assoc mover (dec (count mover)) (rand-int 2))
mover)))
;; Remorseful Prober (Tit For Tat with Random Defection) - Repeat opponent's last choice (ie Tit For Tat),
;; but sometimes probe by defecting in lieu of co-operating. If the opponent defects in response to probing,
;; show remorse by co-operating once.*
(defn RemorsefulProber [mover opponent])
;; Naive Peace Maker (Tit For Tat with Random Co-operation) - Repeat opponent's last choice (ie Tit For Tat),
;; but sometimes make peace by co-operating in lieu of defecting.*
(defn NaivePeaceMaker [mover opponent]
((cond
(empty? mover) coop
(defect? (last opponent)) random
:else coop) mover))
;; True Peace Maker (hybrid of Tit For Tat and Tit For Two Tats with Random Co-operation) -
;; Co-operate unless opponent defects twice in a row, then defect once, but sometimes make peace by co-operating in lieu of defecting.*
(defn TruePeaceMaker [mover opponent]
(let [mover (TitForTwoTats mover opponent)]
(if (defect? (last mover))
(assoc mover (dec (count mover)) (rand-int 2))
mover)))
;; Adaptive - Starts with c,c,c,c,c,c,d,d,d,d,d and then takes choices
;; which have given the best average score re-calculated after every move.
;; Jekyll and Hyde - alternate b/t c and d
;; Pavlov
;; - Cooperate at start and when last choice matches last choice of opponent.
;; - Defect when last choice is different from last choice of opponent
;; end strategies
;; game play
(defn make-move [p1 p2]
(let [{p1-moves :moves p1-strategy :strategy} p1
{p2-moves :moves} p2]
(p1-strategy p1-moves p2-moves)))
(defn player [strategy]
{:moves [] :strategy strategy})
(defn game [p1 p2]
[(assoc p1 :moves (make-move p1 p2))
(assoc p2 :moves (make-move p2 p1))])
(defn iterated-game [iterations player1 player2]
(loop [n 0 [p1 p2] [player1 player2]]
(if (< n iterations)
(recur
(inc n)
(game p1 p2))
[p1 p2])))
;; end game play
;; scoring
(def scores { [1 0] 0
[0 0] 1
[1 1] 3
[0 1] 5 })
(defn score-moves [p1 p2]
(get scores [p1 p2]))
(defn total-scores [p1moves p2moves]
(if (= (count p1moves) (count p2moves))
(reduce
(fn [[p1-sum p2-sum] [p1-move p2-move]]
[(+ p1-sum (score-moves p1-move p2-move))
(+ p2-sum (score-moves p2-move p1-move))])
[0 0]
(partition 2 (interleave p1moves p2moves)))
nil))
;; end scoring
(->> [(player AlwaysRandom) (player AlwaysRandom)]
(apply iterated-game 1)
(map :moves)
(apply total-scores))
# ipd functional
require 'hamster/list'
require 'hamster/hash'
def list; Hamster.list; end
module Prisoner
def prisoner(name, strategy, moves = Hamster.list, scores = Hamster.list)
Hamster.hash(name: name, strategy: strategy, moves: moves, scores: scores)
end
end
module Strategy
def strategy(strategy)
-> mover, other { strategy.(mover[:moves], other[:moves]) }
end
def always_cooperate(mover, other); cooperate(mover); end
def always_defect(mover, other); defect(mover); end
def always_random(mover, other)
Random.new.rand(2) == 0 ? cooperate(mover) : defect(mover)
end
end
module Moves
def cooperate(moves); moves.cons(1); end
def cooperated?(move); move == 1; end
def defect(moves); moves.cons(0); end
def defected?(move); move == 0; end
end
module Game
def game(a, z)
moves_a = a[:strategy].(a, z)
moves_z = z[:strategy].(z, a)
Hamster.list(
a.put(:moves, moves_a),
z.put(:moves, moves_z)
)
end
def iterated_game(iterations, a, z)
(1..iterations).reduce(Hamster.list(a,z)) do |prisoners, _|
game(prisoners.head, prisoners.last)
end
end
def score(a, z)
a.zip(z).reduce(Hamster.hash(a: list, z: list)) do |scores, (move_a, move_z)|
score_a = compute_score(move_a, move_z)
score_z = compute_score(move_z, move_a)
Hamster.hash(
a: scores[:a].cons(score_a),
z: scores[:z].cons(score_z)
)
end
end
def compute_score(a, z)
# f it, let's use state!
@scores ||= {
[0, 1] => 5,
[1, 1] => 3,
[0, 0] => 1,
[1, 0] => 0
}
@scores[[a,z]]
end
end
[Prisoner, Moves, Strategy, Game].each { |m| include m }
a = prisoner(:a, strategy(method(:always_cooperate)))
z = prisoner(:z, strategy(method(:always_defect)))
a, z = iterated_game(10, a, z)
scores = score(a[:moves], z[:moves])
p scores[:a]
p scores[:z]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment