diff --git a/Collective_risk_game120619.ipynb b/Collective_risk_game120619.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..a98cd208eec61d417e53b3a2fc1204419a36c731
--- /dev/null
+++ b/Collective_risk_game120619.ipynb
@@ -0,0 +1,1044 @@
+{
+  "nbformat": 4,
+  "nbformat_minor": 0,
+  "metadata": {
+    "colab": {
+      "name": "Collective-risk-game120619.ipynb",
+      "version": "0.3.2",
+      "provenance": []
+    },
+    "kernelspec": {
+      "name": "python3",
+      "display_name": "Python 3"
+    }
+  },
+  "cells": [
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "-clQqk9qowkW",
+        "colab_type": "text"
+      },
+      "source": [
+        "First, run the model classes."
+      ]
+    },
+    {
+      "cell_type": "code",
+      "metadata": {
+        "id": "qeMe96-ForDP",
+        "colab_type": "code",
+        "colab": {}
+      },
+      "source": [
+        "import matplotlib.pyplot as plt\n",
+        "from matplotlib import cm\n",
+        "from mpl_toolkits.mplot3d import Axes3D\n",
+        "import numpy as np\n",
+        "from scipy import stats\n",
+        "\n",
+        "\n",
+        "class Agent:\n",
+        "\n",
+        "    def __init__(self, rounds, initialWealth, availableActions, alpha=0.1,\n",
+        "                 gamma=0.9, epsilon=0.1, multiArm='greedy'):\n",
+        "\n",
+        "        self.R = rounds\n",
+        "        self.initialWealth = initialWealth\n",
+        "        self.wealth = initialWealth\n",
+        "        self.availableActions = availableActions\n",
+        "        self.iteration = 0\n",
+        "\n",
+        "        \"\"\"initialise Q table to small random numbers\"\"\"\n",
+        "        self.qTable = np.random.rand(self.R, len(self.availableActions)) * 0.01\n",
+        "\n",
+        "        \"Q-Learning Parameters\"\n",
+        "        self.learnRate = alpha\n",
+        "        self.discount  = gamma\n",
+        "        self.epsilon   = epsilon\n",
+        "        self.multiArm  = multiArm\n",
+        "\n",
+        "    def updateReward(self, round, action, loss):\n",
+        "        \"\"\"\n",
+        "        updates the Q-table by receiving a payoff\n",
+        "        \"\"\"\n",
+        "        newWealth = self.wealth * (1-action) * (1-loss)\n",
+        "        reward = newWealth - self.wealth\n",
+        "        self.wealth = newWealth\n",
+        "\n",
+        "        index = self.availableActions.index(action)\n",
+        "        if round == self.R - 1:\n",
+        "            \"\"\" at goal state, no future value\"\"\"\n",
+        "            maxNextQ = 0\n",
+        "        elif round < self.R - 1:\n",
+        "            \"\"\" not at goal state\"\"\"\n",
+        "            maxNextQ = max(self.qTable[round + 1])\n",
+        "        else:\n",
+        "            print(\"ERROR: Illegal round number\")\n",
+        "            exit(2)\n",
+        "\n",
+        "        \"\"\"Update function\"\"\"\n",
+        "        self.qTable[round][index] += self.learnRate * (\n",
+        "                reward + self.discount * maxNextQ - self.qTable[round][index])\n",
+        "\n",
+        "        # if self.iteration == 999:\n",
+        "        #     print(\"QTABLE:\", self.qTable)\n",
+        "\n",
+        "        if round == self.R - 1:\n",
+        "            self.iteration += 1\n",
+        "            # print(\"Player iteration +1 =\", self.iteration)\n",
+        "\n",
+        "\n",
+        "    def chooseAction(self, roundNumber):\n",
+        "        \"\"\"\n",
+        "        Choose an action based on current round number\n",
+        "        :return: an action (float type)\n",
+        "        \"\"\"\n",
+        "        randomAct = False\n",
+        "        if self.multiArm == 'decrease':\n",
+        "            \"\"\"Epsilon Decrease\"\"\"\n",
+        "            if np.random.uniform(0, 1) <= 1 * self.epsilon ** self.iteration:\n",
+        "                randomAct = True\n",
+        "\n",
+        "        elif self.multiArm == 'greedy':\n",
+        "            \"\"\"EPSILON GREEDY\"\"\"\n",
+        "            if np.random.uniform(0, 1) <= self.epsilon:\n",
+        "                randomAct = True\n",
+        "\n",
+        "        if randomAct:\n",
+        "            return np.random.choice(self.availableActions)\n",
+        "        else:\n",
+        "            index = np.argmax(self.qTable[roundNumber])\n",
+        "            return self.availableActions[index]\n",
+        "\n",
+        "    def getStrategy(self):\n",
+        "        \"\"\"\n",
+        "        Get the current strategy without randomness, for analytical use\n",
+        "        :return: a dictionary of actions in all rounds\n",
+        "        \"\"\"\n",
+        "        strategy = {}\n",
+        "        for r in range(self.R):\n",
+        "            index = np.argmax(self.qTable[r])\n",
+        "            strategy[r] = self.availableActions[index]\n",
+        "        return strategy\n",
+        "\n",
+        "\n",
+        "    def getWealth(self):\n",
+        "        return self.wealth\n",
+        "\n",
+        "    def resetWealth(self):\n",
+        "        self.wealth = self.initialWealth\n",
+        "        \n",
+        "        \n",
+        "        \n",
+        "class Graph:\n",
+        "    def __init__(self, N, K, P):\n",
+        "        self.N = N     # Number of players\n",
+        "        self.K = K     # Number of edges/connections each player has originally\n",
+        "        self.P = P     # Rewiring probability\n",
+        "        self.edges = []\n",
+        "        self.selectedNodes = {}\n",
+        "\n",
+        "        if K == N - 1:\n",
+        "            \"\"\"Well-mixed graph, no rewiring\"\"\"\n",
+        "            for i in range(N):\n",
+        "                for j in range(i + 1, N):\n",
+        "                    self.edges.append((i, j))\n",
+        "\n",
+        "        elif K < N - 1:\n",
+        "            assert K % 2 == 0\n",
+        "            k_half = int(K/2)\n",
+        "\n",
+        "            \"\"\"Create the original graph (equal to p = 0)\"\"\"\n",
+        "            for i in range(N):\n",
+        "                for j in range(1, k_half + 1):\n",
+        "                    self.edges.append((i, (i + j) % N))\n",
+        "\n",
+        "            \"\"\"Randomly rewire each edge with prob p, start from distance 1\"\"\"\n",
+        "            for j in range(1, k_half + 1):\n",
+        "                for i in range(N):\n",
+        "                    if P > np.random.uniform(0, 1):\n",
+        "                        new_set = [v for v in range(N) if v != i and (i, v) not\n",
+        "                                   in self.edges and (v, i) not in self.edges]\n",
+        "                        if len(new_set) > 0:\n",
+        "                            new = np.random.choice(new_set)\n",
+        "                            self.edges.append((i, new))\n",
+        "                            old = (i + j) % self.N\n",
+        "                            self.edges.remove((i, old))\n",
+        "                            # print(\"Rewiring (\", i, old, \") to: (\", i, new)\n",
+        "\n",
+        "        else:\n",
+        "            print(\"ERROR: Illegal K or N value.\")\n",
+        "            exit(3)\n",
+        "\n",
+        "    def select(self):\n",
+        "        \"\"\"\n",
+        "        Randomly select edges from the graph, so that each player is drawn at\n",
+        "        least once.\n",
+        "        :return: A list of tuples, containing players' index\n",
+        "        \"\"\"\n",
+        "        edges = self.edges\n",
+        "        nodes = list(range(self.N))\n",
+        "        select = []\n",
+        "        selectedNodes = {i: 0 for i in range(self.N)}\n",
+        "\n",
+        "        if self.K == self.N - 1:     #Well-mixed graph\n",
+        "            permutation = np.random.permutation(self.N)\n",
+        "            selectedNodes = {i: 1 for i in range(self.N)}\n",
+        "            if self.N % 2 == 1:\n",
+        "                extraNode = np.random.randint(0, self.N)\n",
+        "                while extraNode == permutation[self.N - 1]:\n",
+        "                    extraNode = np.random.randint(0, self.N)\n",
+        "                permutation = np.append(permutation, extraNode)\n",
+        "                selectedNodes[extraNode] += 1\n",
+        "\n",
+        "            select = permutation.reshape((int(len(permutation)/2), 2))\n",
+        "        else:\n",
+        "            while edges:       # Loop when edges is not empty\n",
+        "                i, j = edges[np.random.randint(0, len(edges))]\n",
+        "                # print(\"selected nodes:\", i, j)\n",
+        "                select.append((i, j))\n",
+        "                nodes.remove(i)\n",
+        "                nodes.remove(j)\n",
+        "                selectedNodes[i] += 1\n",
+        "                selectedNodes[j] += 1\n",
+        "                # print(\"Remaining nodes:\", nodes)\n",
+        "                edges = [(a, b) for (a, b) in edges if (a != i) and (a != j)\n",
+        "                         and (b != i) and (b != j)]\n",
+        "                # print(\"after removal\", edges)\n",
+        "\n",
+        "            while nodes:\n",
+        "                v = nodes.pop(np.random.randint(0, len(nodes)))\n",
+        "                v_edges = [(i, j) for (i, j) in self.edges if i == v or j == v]\n",
+        "                i, j = v_edges[np.random.randint(len(v_edges))]\n",
+        "                select.append((i, j))\n",
+        "                selectedNodes[i] += 1\n",
+        "                selectedNodes[j] += 1\n",
+        "\n",
+        "            # print(\"Number of each nodes selected:\", selectedNodes)\n",
+        "        self.selectedNodes = selectedNodes\n",
+        "        return select\n",
+        "\n",
+        "\n",
+        "    def getNodesNumber(self):\n",
+        "        \"\"\"\n",
+        "        :return: A dictionary specify how many times each player are drawn from\n",
+        "        the last select()\n",
+        "        \"\"\"\n",
+        "        return self.selectedNodes\n",
+        "\n",
+        "    def getEdgeList(self):\n",
+        "        return self.edges\n",
+        "      \n",
+        "\n",
+        "      \n",
+        "class Game:\n",
+        "    def __init__(self, N=100, R=1, K=99, P=0, Actions=[0, 0.2, 0.4, 0.6, 0.8],\n",
+        "                 I=1000, RF=0, alpha=1, epsilon=0.1,multiArm='greedy',\n",
+        "                 threshold=0.8):\n",
+        "        self.N = N\n",
+        "        self.M = 2\n",
+        "        self.RF = RF\n",
+        "        self.alpha = alpha\n",
+        "        self.R = R\n",
+        "        self.threshold = threshold\n",
+        "        self.actions = Actions\n",
+        "        self.iterations = I\n",
+        "\n",
+        "        \"\"\"\n",
+        "        | 2-Player Game Graph Model: (Small-world network)\n",
+        "        |\n",
+        "        | P: Probability of rewiring each original edge in the graph\n",
+        "        |\n",
+        "        | K: The number of edges(games) connected to each player. Has to be an \n",
+        "        |    even number. Max k: n - 2 (for even n) | n - 1 (for odd n)\n",
+        "        |    k can only be odd as n - 1 (for all n). In cases k = n - 1 -> a \n",
+        "        |    fully connected graph\n",
+        "        \"\"\"\n",
+        "        self.rewire_p = P\n",
+        "        self.rewire_k = K\n",
+        "        # assert (self.rewire_k < self.N)\n",
+        "        self.graph = Graph(self.N, self.rewire_k, self.rewire_p)\n",
+        "\n",
+        "        \"Create players\"\n",
+        "        self.players = []\n",
+        "        IW = 100  # Initial Wealth\n",
+        "        self.totalWealth = self.M * IW\n",
+        "        for i in range(self.N):\n",
+        "            self.players.append(Agent(self.R, IW, self.actions,\n",
+        "                                      epsilon=epsilon, multiArm=multiArm))\n",
+        "\n",
+        "    def riskfunc(self, contribution, totalwealth):\n",
+        "        \"\"\"\n",
+        "        Implemented different risk functions here.\n",
+        "        :return: the probability of disaster happening, given contribution\n",
+        "        and total wealth\n",
+        "        \"\"\"\n",
+        "\n",
+        "        proportion = contribution / totalwealth\n",
+        "\n",
+        "        if self.RF == 0:\n",
+        "            # probably parse more parameters here\n",
+        "            return 1 - proportion\n",
+        "\n",
+        "\n",
+        "        elif self.RF == 1:\n",
+        "            if proportion >= self.threshold:\n",
+        "                return 0\n",
+        "            else:\n",
+        "                return 1\n",
+        "\n",
+        "\n",
+        "        elif self.RF == 2:\n",
+        "\n",
+        "            if proportion < self.threshold:\n",
+        "                return 1 - proportion / self.threshold\n",
+        "            else:\n",
+        "                return 0\n",
+        "\n",
+        "        return \"error\"\n",
+        "\n",
+        "    def play(self):\n",
+        "        \"\"\"\n",
+        "        Play a whole trial of I (1000) iterations, N (100) players games\n",
+        "        :return: a 3d numpy matrix, recording the averaged counted number of\n",
+        "        all actions in each round in all iterations.\n",
+        "        \"\"\"\n",
+        "\n",
+        "        results = np.zeros((self.iterations, self.R, len(self.actions)))\n",
+        "        \"\"\"ITERATION\"\"\"\n",
+        "        for iter in range(self.iterations):\n",
+        "            # print(\"GAME ITERATION\", iter)\n",
+        "\n",
+        "            actionTable = np.zeros((self.N, self.R))\n",
+        "            strategyTable = np.zeros((self.R, self.N))  # DIFFERENT AXIS R-N\n",
+        "            lossTable = np.zeros((self.N, self.R))\n",
+        "\n",
+        "            for playerIndex in range(self.N):  # For each player\n",
+        "                player = self.players[playerIndex]\n",
+        "                player.resetWealth()  # reset initial wealth\n",
+        "                for r in range(self.R):  # For each round\n",
+        "                    action = player.chooseAction(r)\n",
+        "                    actionTable[playerIndex][r] = action\n",
+        "                    strategyTable[r][playerIndex] = player.getStrategy()[r]\n",
+        "\n",
+        "            playersNo = self.graph.select()\n",
+        "            for r in range(self.R):\n",
+        "                for [i, j] in playersNo:\n",
+        "                    pool = 0\n",
+        "                    pool += self.players[i].getWealth() * actionTable[i][r] + \\\n",
+        "                            self.players[j].getWealth() * actionTable[j][r]\n",
+        "                    risk = self.riskfunc(pool, self.totalWealth)\n",
+        "\n",
+        "                    for p in [i, j]:\n",
+        "                        if np.random.uniform(0, 1) < risk:\n",
+        "                            lossTable[p, r] += self.alpha / \\\n",
+        "                                               self.graph.getNodesNumber()[p]\n",
+        "                for i in range(self.N):\n",
+        "                    self.players[i].updateReward(r, actionTable[i][r],\n",
+        "                                                 lossTable[i][r])\n",
+        "\n",
+        "            for r in range(self.R):\n",
+        "                unique, count = np.unique(strategyTable[r], return_counts=True)\n",
+        "                round_counter = dict(zip(unique, count))\n",
+        "                # print(\"Round \", r, round_counter)\n",
+        "\n",
+        "                for a in range(len(self.actions)):\n",
+        "                    if self.actions[a] not in round_counter:\n",
+        "                        pass\n",
+        "                    else:\n",
+        "                        results[iter, r, a] = round_counter[self.actions[a]]\n",
+        "\n",
+        "        return results"
+      ],
+      "execution_count": 0,
+      "outputs": []
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "0rTn1unXotNt",
+        "colab_type": "text"
+      },
+      "source": [
+        "Then, run the supporting graphic and t-test definitions."
+      ]
+    },
+    {
+      "cell_type": "code",
+      "metadata": {
+        "id": "m8B4PBOUpsZe",
+        "colab_type": "code",
+        "colab": {}
+      },
+      "source": [
+        "def stackPlot(data, r, Actions, Iterations, legendLoc='best', titleComment=\"\"):\n",
+        "    \"\"\"\n",
+        "    Draw a stack plot from averaged data of round r.\n",
+        "    \"\"\"\n",
+        "    A = len(Actions)\n",
+        "    x = range(Iterations)\n",
+        "    y = np.zeros((Iterations, A))\n",
+        "    for i in range(Iterations):\n",
+        "        y[i] = data[i][r]\n",
+        "    y = np.vstack(y.T)\n",
+        "\n",
+        "    fig, ax = plt.subplots()\n",
+        "    ax.stackplot(x, y, labels=Actions, colors=[str(0.9 - 0.9 * x) for x in\n",
+        "                                               Actions])\n",
+        "    ax.legend(loc=legendLoc)\n",
+        "    plt.ylabel('Percentage of each action')\n",
+        "    plt.xlabel('Time(iterations)')\n",
+        "\n",
+        "    title = 'Average Composition of Actions in Round ' + str(r + 1)\n",
+        "    if titleComment:\n",
+        "        title += \"\\n\" + titleComment\n",
+        "\n",
+        "    plt.title(title)\n",
+        "\n",
+        "    # plt.savefig(titleComment + \" in round \" + str(r+1) + \".png\")\n",
+        "\n",
+        "    plt.show()\n",
+        "\n",
+        "\n",
+        "def rep(repeat=30, R=1, Actions=[0, 0.2, 0.4, 0.6, 0.8], I=1000, **kwargs):\n",
+        "    \"\"\"\n",
+        "    Repeat the game over (30) trials and retrieve the average data of\n",
+        "    game.play()\n",
+        "    :return: Averaged game results, same shape as the return of game.play()\n",
+        "    \"\"\"\n",
+        "    data = np.zeros((I, R, len(Actions)))\n",
+        "    Actions.sort()\n",
+        "    for re in range(repeat):\n",
+        "#         print(\"REP\", re)\n",
+        "        g = Game(R=R, Actions=Actions, I=I, **kwargs)\n",
+        "        result = g.play()\n",
+        "        data += result\n",
+        "    data /= repeat\n",
+        "    return data\n",
+        "\n",
+        "\n",
+        "def averageOfLast(data, Actions, N=100, r=0, lastIterations=100):\n",
+        "    \"\"\"\n",
+        "    Averaged contribution and action counter of last (100) iterations from the\n",
+        "    data produced by rep()\n",
+        "    :return: a tuple: (average contribution, a dictionary as action counter)\n",
+        "    \"\"\"\n",
+        "    sum = 0\n",
+        "    action_counter = {action: 0 for action in Actions}\n",
+        "\n",
+        "    for i in range(-1, -lastIterations - 1, -1):\n",
+        "        sum += np.sum(data[i, r] * Actions)\n",
+        "        for a in range(len(Actions)):\n",
+        "            action_counter[Actions[a]] += data[i, r, a] / lastIterations\n",
+        "    return (sum / (lastIterations * N), action_counter)\n",
+        "\n",
+        "\n",
+        "def graph_kp3d(Actions, Klist=[2, 4, 8, 10], Plist=[0, 0.3, 0.6, 0.9],\n",
+        "               repeat=30, N=100, **kwargs):\n",
+        "    \"\"\"\n",
+        "    Draw a 3D graph for graph-based model, showing the effect of K and P on\n",
+        "    average contributions. (No effect observed)\n",
+        "    \"\"\"\n",
+        "\n",
+        "    K = Klist\n",
+        "    P = Plist\n",
+        "    meanA = np.zeros((len(K), len(P)))\n",
+        "\n",
+        "    for k in range(len(K)):\n",
+        "        for p in range(len(P)):\n",
+        "            data = rep(repeat, K=K[k], P=P[p], Actions=Actions, **kwargs)\n",
+        "            meanA[k][p] = averageOfLast(data, Actions, lastIterations=100,\n",
+        "                                        N=N)[0]\n",
+        "            print(\"k, p, mean\", k, p, meanA[k][p])\n",
+        "\n",
+        "    P, K = np.meshgrid(P, K)\n",
+        "\n",
+        "    fig = plt.figure()\n",
+        "    ax = fig.gca(projection='3d')\n",
+        "\n",
+        "    surf = ax.plot_surface(P, K, meanA, cmap=cm.coolwarm,\n",
+        "                           linewidth=0, antialiased=False)\n",
+        "\n",
+        "    fig.colorbar(surf, shrink=0.5, aspect=5)\n",
+        "    plt.show()\n",
+        "\n",
+        "\n",
+        "def graph3d_alpha_threshold(Actions, repeat=30,\n",
+        "                            AlphaList=np.arange(0, 1.01, 0.05),\n",
+        "                            ThreshList=np.arange(0.1, 1.05, 0.05),\n",
+        "                            N=100, **kwargs):\n",
+        "    \"\"\"\n",
+        "    Draw two 3D graphs showing the average contribution and the average\n",
+        "    contribution divided by threshold on two parameters: alpha and threshold\n",
+        "    \"\"\"\n",
+        "    mean = np.zeros((len(ThreshList), len(AlphaList)))\n",
+        "    ratio_by_threshold = np.zeros((len(ThreshList), len(AlphaList)))\n",
+        "\n",
+        "    for t in range(len(ThreshList)):\n",
+        "        for a in range(len(AlphaList)):\n",
+        "            print(\"Calculating... t, alpha = \", t, a)\n",
+        "            data = rep(repeat=repeat, Actions=Actions, alpha=AlphaList[a],\n",
+        "                       threshold=ThreshList[t], **kwargs)\n",
+        "            mean[t][a] = averageOfLast(data, Actions, lastIterations=100,\n",
+        "                                       N=N)[0]\n",
+        "        ratio_by_threshold[t] = mean[t] / ThreshList[t]\n",
+        "\n",
+        "    A, T = np.meshgrid(AlphaList, ThreshList)\n",
+        "\n",
+        "    fig = plt.figure()\n",
+        "    ax = fig.gca(projection='3d')\n",
+        "\n",
+        "    surf = ax.plot_surface(A, T, mean, cmap=cm.Greys,\n",
+        "                           linewidth=0, antialiased=False)\n",
+        "    ax.set_xlabel('Alpha')\n",
+        "    # ax.invert_xaxis()\n",
+        "    ax.set_ylabel('Threshold')\n",
+        "    ax.set_zlabel('Average contribution')\n",
+        "    fig.colorbar(surf, shrink=0.5, aspect=5)\n",
+        "    # plt.show()\n",
+        "\n",
+        "\n",
+        "    fig2 = plt.figure()\n",
+        "    ax2 = fig2.gca(projection='3d')\n",
+        "    surf2 = ax2.plot_surface(A, T, ratio_by_threshold, cmap=cm.Greys,\n",
+        "                           linewidth=0, antialiased=False)\n",
+        "    ax2.set_xlabel('Alpha')\n",
+        "    # ax.invert_xaxis()\n",
+        "    ax2.set_ylabel('Threshold')\n",
+        "    # ax.invert_yaxis()\n",
+        "    ax2.set_zlabel('Average contribution by threshold')\n",
+        "    fig2.colorbar(surf2, shrink=0.5, aspect=5)\n",
+        "    plt.show()\n",
+        "\n",
+        "\n",
+        "def stackBar(r, Actions, repeat=30, multiArm='greedy', legendLoc='best',\n",
+        "             **kwargs):\n",
+        "    \"\"\"\n",
+        "    Draw a stack bar graph, to compare the composition of actions on one\n",
+        "    parameter, specified as a list in **kwargs\n",
+        "    \"\"\"\n",
+        "\n",
+        "    key = -1\n",
+        "    alist = []\n",
+        "    for k, v in kwargs.items():\n",
+        "        if isinstance(v, list):\n",
+        "            if key == -1:\n",
+        "                key = k\n",
+        "                alist = v\n",
+        "            else:\n",
+        "                print(\"ERROR, Stack Bar Graph Expects Only 1 List to Compare\")\n",
+        "                exit(4)\n",
+        "    del kwargs[key]\n",
+        "\n",
+        "    print(\"Comparing:\", key)\n",
+        "    print(\"On:\", alist)\n",
+        "\n",
+        "    A = len(Actions)\n",
+        "    p = []\n",
+        "    count = np.zeros((A, len(alist)))  # of each action in each iter\n",
+        "    ind = np.arange(len(alist))\n",
+        "    width = 0.3\n",
+        "\n",
+        "    for al in range(len(alist)):\n",
+        "        newKwargs = {**{key: alist[al]}, **kwargs}\n",
+        "        if key == 'N':\n",
+        "            newKwargs['K'] = alist[al] - 1\n",
+        "        elif 'N' not in newKwargs.keys():\n",
+        "            newKwargs['N'] = 100  # default value\n",
+        "        data = rep(repeat, Actions=Actions, multiArm=multiArm, **newKwargs) /\\\n",
+        "               newKwargs['N'] * 100\n",
+        "        action_counter = averageOfLast(data, Actions, r=r,\n",
+        "                                       lastIterations=100)[1]\n",
+        "        for a in range(A):\n",
+        "            count[a, al] = action_counter[Actions[a]]\n",
+        "    base = 0\n",
+        "\n",
+        "    for a in range(A):\n",
+        "        p.append(plt.bar(ind, count[a], width, bottom=base,\n",
+        "                         color=str(0.9 - 0.9 * Actions[a])))\n",
+        "        base += count[a]\n",
+        "\n",
+        "    plt.ylabel('Percentage of Actions')\n",
+        "    if key == 'epsilon':\n",
+        "        plt.xlabel(key + ' (' + multiArm + ')')\n",
+        "    else:\n",
+        "        plt.xlabel(key)\n",
+        "    plt.title('Average Composition of Actions in Round ' + str(r + 1))\n",
+        "    plt.xticks(ind, alist)\n",
+        "    plt.yticks(np.arange(0, 101, 10))\n",
+        "    plt.legend(tuple([p[x][0] for x in range(A)][::-1]), tuple(Actions[::-1]),\n",
+        "               loc=legendLoc)\n",
+        "    plt.show()\n",
+        "\n",
+        "\n",
+        "def t_test(repeat, Actions, r=0, R=1, I=1000, lastIterations=100, N=100,\n",
+        "           byThreshold=False, **kwargs):\n",
+        "    \"\"\"\n",
+        "    Compute the p-value of average contributions on two values of one\n",
+        "    parameter, specified as a tuple in **kwargs\n",
+        "    \"\"\"\n",
+        "    key = -1\n",
+        "    atuple = ()\n",
+        "    for k, v in kwargs.items():\n",
+        "        if isinstance(v, tuple):\n",
+        "            if key == -1:\n",
+        "                key = k\n",
+        "                atuple = v\n",
+        "            else:\n",
+        "                print(\"ERROR, T-Test Expects Only 1 Tuple to Compare\")\n",
+        "                exit(5)\n",
+        "    del kwargs[key]\n",
+        "\n",
+        "    samples = np.zeros((2, repeat))\n",
+        "\n",
+        "    for s in (0, 1):\n",
+        "        newArgs = {**{key: atuple[s]}, **kwargs}\n",
+        "        samples[s] = repHist(repeat, Actions, R, r, I, lastIterations, N,\n",
+        "                             **newArgs)\n",
+        "        if byThreshold:\n",
+        "            samples[s] /= newArgs[\"threshold\"]\n",
+        "        print(\"Sample\", s, samples[s])\n",
+        "\n",
+        "    print(stats.ttest_ind(samples[0], samples[1]))\n",
+        "\n",
+        "\n",
+        "def repHist(repeat, Actions, R=1, r=0, I=1000, lastIterations=100, N=100,\n",
+        "            **kwargs):\n",
+        "    \"\"\"\n",
+        "    :return: A list of average contributions of all repetitions\n",
+        "    \"\"\"\n",
+        "    hist = np.zeros(repeat)\n",
+        "    for re in range(repeat):\n",
+        "        # print(\"HistREP\", re)\n",
+        "        g = game.Game(R=R, Actions=Actions, I=I, N=N, **kwargs)\n",
+        "        result = g.play()\n",
+        "        hist[re] = averageOfLast(result, Actions, N, r, lastIterations)[0]\n",
+        "    return hist"
+      ],
+      "execution_count": 0,
+      "outputs": []
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "ObdagnBgpzed",
+        "colab_type": "text"
+      },
+      "source": [
+        "Before generating results, define the default values.\n",
+        "* lower repeat value to yield faster results, but less accurate\n",
+        "\n"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "metadata": {
+        "id": "-BQBZ9qOqCa3",
+        "colab_type": "code",
+        "colab": {}
+      },
+      "source": [
+        "N = 100\n",
+        "R = 1\n",
+        "K = 99\n",
+        "P = 0\n",
+        "I = 1000\n",
+        "RF = 0\n",
+        "alpha = 1\n",
+        "Actions = [0, 0.2, 0.4, 0.6, 0.8]\n",
+        "repeat = 30"
+      ],
+      "execution_count": 0,
+      "outputs": []
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "9CzS0FO6qNW-",
+        "colab_type": "text"
+      },
+      "source": [
+        "Figure 2:"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "metadata": {
+        "id": "Pbu5TljSqXun",
+        "colab_type": "code",
+        "colab": {}
+      },
+      "source": [
+        "data = rep(repeat=repeat, N=100, alpha=0.8, R=8)\n",
+        "stackPlot(data, r=0, Iterations=I, Actions=Actions, legendLoc='lower right')"
+      ],
+      "execution_count": 0,
+      "outputs": []
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "iqNPxiiMqXAR",
+        "colab_type": "text"
+      },
+      "source": [
+        "Figure 3:"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "metadata": {
+        "id": "VOrAvfEMrFwN",
+        "colab_type": "code",
+        "colab": {}
+      },
+      "source": [
+        "stackBar(0, Actions, repeat=repeat,\n",
+        "         alpha=[0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1],\n",
+        "         legendLoc='lower left')"
+      ],
+      "execution_count": 0,
+      "outputs": []
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "FvfkveaVr1yq",
+        "colab_type": "text"
+      },
+      "source": [
+        "Figure 4:"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "metadata": {
+        "id": "yhHJH0N8r1DE",
+        "colab_type": "code",
+        "colab": {}
+      },
+      "source": [
+        "data = rep(repeat, R=8)\n",
+        "for r in [0, 1, 3]:\n",
+        "    stackPlot(data, r, Actions, I, legendLoc='lower right')"
+      ],
+      "execution_count": 0,
+      "outputs": []
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "iOZCEyuZr0FH",
+        "colab_type": "text"
+      },
+      "source": [
+        "Figure 5:"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "metadata": {
+        "id": "KM96KjnQsDMJ",
+        "colab_type": "code",
+        "colab": {}
+      },
+      "source": [
+        "stackBar(0, Actions, repeat=repeat,\n",
+        "         threshold=[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1],\n",
+        "         legendLoc='upper left', RF=2)"
+      ],
+      "execution_count": 0,
+      "outputs": []
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "MWqvxW5ssTGW",
+        "colab_type": "text"
+      },
+      "source": [
+        "Figure 6:"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "metadata": {
+        "id": "qOggilpGsSy1",
+        "colab_type": "code",
+        "colab": {}
+      },
+      "source": [
+        "data = rep(repeat=repeat, Actions=Actions, R=1, I=I, RF=2, threshold=0.2)\n",
+        "stackPlot(data, r=0, Iterations=I, Actions=Actions,\n",
+        "          titleComment=\"threshold = 0.2\")"
+      ],
+      "execution_count": 0,
+      "outputs": []
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "tZzJYF6OsZK1",
+        "colab_type": "text"
+      },
+      "source": [
+        "Figure 7 & 8 : 3D graph comparing alpha and threshold"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "metadata": {
+        "id": "LGbBOdpzsY8V",
+        "colab_type": "code",
+        "colab": {}
+      },
+      "source": [
+        "graph3d_alpha_threshold(Actions, repeat=repeat, RF=2)"
+      ],
+      "execution_count": 0,
+      "outputs": []
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "JDxeu4KesjN-",
+        "colab_type": "text"
+      },
+      "source": [
+        "Figure 9 : simple line graph comparing alpha for threshold=0.2"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "metadata": {
+        "id": "6GZkRJ6EsjBL",
+        "colab_type": "code",
+        "colab": {}
+      },
+      "source": [
+        "alphaList = np.arange(0, 1.01, 0.02)\n",
+        "mean = np.zeros(len(alphaList))\n",
+        "for i in range(len(alphaList)):\n",
+        "    data = rep(repeat, alpha=alphaList[i], threshold=0.2, RF=2)\n",
+        "    mean[i] = averageOfLast(data, Actions)[0]\n",
+        "\n",
+        "plt.plot([0.2, 0.2], '--', color='0.5')\n",
+        "plt.plot(alphaList, mean, color='black')\n",
+        "\n",
+        "plt.ylabel('Average Contributions')\n",
+        "plt.xlabel('Alpha, the loss fraction')\n",
+        "plt.show()"
+      ],
+      "execution_count": 0,
+      "outputs": []
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "ENMPhvNwsvYp",
+        "colab_type": "text"
+      },
+      "source": [
+        "Figure 10 : Comparing composition on epsilon (greedy)"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "metadata": {
+        "id": "VW3O-JWfs0nE",
+        "colab_type": "code",
+        "colab": {}
+      },
+      "source": [
+        "stackBar(0, Actions, repeat=repeat, multiArm='greedy',\n",
+        "         legendLoc='lower right',\n",
+        "         epsilon=[0.05, 0.1, 0.15, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8,0.9])"
+      ],
+      "execution_count": 0,
+      "outputs": []
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "Z_SsvVFos93T",
+        "colab_type": "text"
+      },
+      "source": [
+        "Figure 11 : Extending 0.2-greedy to 5000 iterations"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "metadata": {
+        "id": "ZYVSFfKys5MY",
+        "colab_type": "code",
+        "colab": {}
+      },
+      "source": [
+        "data = rep(repeat=repeat, Actions=Actions, multiArm='greedy',\n",
+        "           epsilon=0.2, I=5000)\n",
+        "stackPlot(data, r=0, Iterations=5000, Actions=Actions,\n",
+        "          titleComment=\"0.2 - greedy\", legendLoc='lower left')"
+      ],
+      "execution_count": 0,
+      "outputs": []
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "pBIM37BHtP0Q",
+        "colab_type": "text"
+      },
+      "source": [
+        "Figure 12 : Comparing composition on epsilon (decrease)"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "metadata": {
+        "id": "bexqMZLttPkZ",
+        "colab_type": "code",
+        "colab": {}
+      },
+      "source": [
+        "stackBar(0, Actions, repeat=repeat, multiArm='decrease',\n",
+        "         legendLoc='lower left',\n",
+        "         epsilon=[0.1, 0.4, 0.8, 0.9, 0.95, 0.98, 0.99, 0.999, 0.9999])"
+      ],
+      "execution_count": 0,
+      "outputs": []
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "s-7lBnTMtZBF",
+        "colab_type": "text"
+      },
+      "source": [
+        "Figure 13 : Extending 0.999-decrease to 5000 iterations"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "metadata": {
+        "id": "o3P9yUCXtY3Y",
+        "colab_type": "code",
+        "colab": {}
+      },
+      "source": [
+        "data = rep(repeat=repeat, Actions=Actions, multiArm='decrease',\n",
+        "           epsilon=0.999, I=5000)\n",
+        "stackPlot(data, r=0, Iterations=5000, Actions=Actions,\n",
+        "          titleComment=\"0.999 - decrease\", legendLoc='lower left')"
+      ],
+      "execution_count": 0,
+      "outputs": []
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "CyJQ8h4ptgR6",
+        "colab_type": "text"
+      },
+      "source": [
+        "T-tests 1: Average contribution of different T"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "metadata": {
+        "id": "6OxMY-g9tgHp",
+        "colab_type": "code",
+        "colab": {}
+      },
+      "source": [
+        "t_test(30, Actions, alpha=1, RF=2, threshold=(0.9, 1.0))    #p=0.2708\n",
+        "t_test(30, Actions, alpha=1, RF=2, threshold=(0.8, 1.0))    #p=0.1096\n",
+        "t_test(30, Actions, alpha=1, RF=2, threshold=(0.7, 1.0))    #p=0.1633\n",
+        "t_test(30, Actions, alpha=1, RF=2, threshold=(0.6, 1.0))    #p=0.2208\n",
+        "\n",
+        "t_test(30, Actions, alpha=1, RF=2, threshold=(0.5, 1.0))    #p=2.2067e-08"
+      ],
+      "execution_count": 0,
+      "outputs": []
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "MXQ3ac_ptsUu",
+        "colab_type": "text"
+      },
+      "source": [
+        "T-test 2 : Average contribution of different alpha when T=0.2"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "metadata": {
+        "id": "QSvs63Xsts3Z",
+        "colab_type": "code",
+        "colab": {}
+      },
+      "source": [
+        "base = repHist(30, Actions, alpha=1, RF=2, threshold=0.2)\n",
+        "for alpha in np.arange(0.8, 1, 0.01):\n",
+        "    compare = repHist(30, Actions, alpha=alpha, RF=2, threshold=0.2)\n",
+        "    print(\"Alpha=\", alpha, stats.ttest_ind(base, compare))"
+      ],
+      "execution_count": 0,
+      "outputs": []
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "vsnklN-xtz_r",
+        "colab_type": "text"
+      },
+      "source": [
+        "T-test 3 : Avg contribution of Epsilon-decrease 0.99 with 0.1 and 0.999"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "metadata": {
+        "id": "MWKw3TGTtze3",
+        "colab_type": "code",
+        "colab": {}
+      },
+      "source": [
+        "base = repHist(30, Actions, multiArm='decrease', epsilon=0.99)\n",
+        "for epsilon in (0.1, 0.999):\n",
+        "    compare = repHist(30, Actions, multiArm='decrease', epsilon=epsilon)\n",
+        "    print(\"Epsilon=\", epsilon, stats.ttest_ind(base, compare))"
+      ],
+      "execution_count": 0,
+      "outputs": []
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "AzunuVd9t92Q",
+        "colab_type": "text"
+      },
+      "source": [
+        "   T-test 4. Avg contribution of 0.999-decrease 5000 iterations with 0.9"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "metadata": {
+        "id": "rN-QXQxJuDOy",
+        "colab_type": "code",
+        "colab": {}
+      },
+      "source": [
+        "base = repHist(30, Actions, multiArm='decrease', epsilon=0.9)\n",
+        "compare = repHist(30, Actions, multiArm='decrease', epsilon=0.999, I=5000)\n",
+        "print(stats.ttest_ind(base, compare))"
+      ],
+      "execution_count": 0,
+      "outputs": []
+    }
+  ]
+}
\ No newline at end of file