Jekyll2018-02-21T17:40:33+00:00/BlogThoughts and ideas
Clojure’s core.async - an introduction2018-01-06T16:16:36+00:002018-01-06T16:16:36+00:00/2018/01/06/clojure-core-async<p>Clojure’s core.async is a library that enables Clojure developers to build async programs. In this post we briefly introduce the principles behind core.async, namely CSP.</p>
<h2 id="csp">CSP</h2>
<p>Clojure’s core.async is based on the concept of <a href="https://en.wikipedia.org/wiki/Communicating_sequential_processes">Communicating Sequential Processes (CSP)</a>, an idea first presented by Robert Hoare in 1978. The impetus behind CSP was that processes need a way to communicate and synchronize with each other, and doing so via shared memory and locks is error-prone. (Besides CSP, actors are the other popular paradigm to address this problem.)</p>
<p>CSP instead suggests that processes communicate with each other via a mechanism called a <em>channel</em>. Processes can put a message onto a channel or they can take messages from a channel. In its simplest form, the putting process waits until the receiving process is ready to take, and vice versa. There can be multiple writers and readers on a channel, and neither knows about the other.</p>
<p>Another idea from CSP is the ability to choose from a variety of alternate inputs, or outputs. A thread can take the first value from multiple channels, whichever is ready first (or, a channel can put to one of many channels, whichever is ready to receive first).</p>
<p>Clojure’s core.async builds on these general ideas, with a few nuances. First, the processes in reality are threads, or an even lighter-weight construct that generally imitates the idea of a thread (referred to as go blocks, or IOC threads).</p>
<p>The other notable feature is that Clojure enables channel buffering, with the requirement that the buffers be bounded.</p>
<h2 id="example">Example</h2>
<p>Below is a simple example that utilizes the following constructs from core.async:</p>
<ul>
<li><code class="highlighter-rouge">(chan)</code> returns an unbuffered channel</li>
<li><code class="highlighter-rouge">(>!! ch message)</code> puts a given message on a channel. Blocks until a receiver is ready</li>
<li><code class="highlighter-rouge">(<!! ch)</code> takes a channel and returns the next message from the channel. Blocks until a message is delivered to the channel</li>
<li><code class="highlighter-rouge">(thread & body)</code> takes a body and executes the body on a separate thread</li>
</ul>
<h3 id="simple-put-receive">Simple put-receive</h3>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(defn- put-task [c]
(Thread/sleep 1000)
(println "Putting message on channel")
(>!! c "test message"))
(defn example []
(let [c (chan)]
(thread (put-task c))
(println "Started thread")
(->> (<!! c)
(str "Received message from channel: ")
(println))
(close! c)))
user=> (example)
"Started thread"
"Putting message on channel"
"Received message from channel: test message"
</code></pre></div></div>Clojure’s core.async is a library that enables Clojure developers to build async programs. In this post we briefly introduce the principles behind core.async, namely CSP.Concurrency primitives in Clojure2017-12-07T16:16:36+00:002017-12-07T16:16:36+00:00/2017/12/07/concurrency-primitives-clojure<p>Whether we are aware of it or not, we are intuitively familiar with the concept of concurrency. If we have ever followed a recipe, we know that when we perform a task, there’s often some freedom in how we structure the steps. If a recipe asks us to chop onions, carrots, and peppers, we know that we don’t have to chop them exactly in this order - we can choose to chop the peppers first, or alternate between all three, if we so choose. And this is the very definition of <em>concurrency</em> - the ability to perform certain steps of a task in non-sequential order.</p>
<h4 id="parallelism">Parallelism</h4>
<p>Now, we may not see much benefit to chopping vegetables in a different order - we only have one set of hands, so it will take the same time. But if we had a friend to help us, our friend could chop carrots at the same time as we chop onions, and reduce the overall chopping time. This is a specific form of concurrency known as <em>parallelism</em>.</p>
<h4 id="asynchrony">Asynchrony</h4>
<p>These concepts both relate to work we do with our hands - chopping vegetables, or mixing things. But what about other kinds of work, say pre-heating the oven? We can pre-heat the oven and start doing other tasks before waiting for it to complete. The ability to not “block” on a task and perform other tasks while the prior task is carrying forward is a concept known as <em>asynchrony</em>. While not directly related to concurrency, it relates to how we perform work, and we often see these two concepts talked about together.</p>
<h2 id="task-relationships">Task relationships</h2>
<p>So we see that when we perform a task, we often have flexibility in how we structure the steps (which we can call “sub-tasks”). Some sub-tasks can be done out of order (for example, chopping the vegetables), but some must be done in order (for example, we must finish chopping before putting everything in the oven). How do we encode this information in our programs, which are a set of instructions for performing a given task? Or, put another way, how do we convey the relationships between sub-tasks?</p>
<p>The first thing we must determine is if the result of a given task will be used in some other task down the line. If not, our job is pretty easy - we can specify the task and forget about it. An example might be if we want to write something to a log file without caring whether it fails.</p>
<p>Most often, however, we will want to use the result of the sub-task later on. When we move from a stage where tasks can be completed in any order, to a stage that requires the results of these previous tasks, we reach a boundary where we must synchronize the tasks. Indeed, in mathematics, the word concurrency is used to describe lines that intersect at a point.</p>
<p><img src="/assets/concurrent_lines.jpg" alt="alt text" /></p>
<h2 id="clojure-primitives-for-concurrency">Clojure primitives for concurrency</h2>
<p>How do we handle this synchronization, when we have no guarantees when the sub-tasks will complete? The mechanisms that Clojure provides are: futures, promises, and delay.</p>
<p>The first thing to remember is that these primitives are various implementations of the same functionality - to coordinate multiple tasks, or threads of execution. They differ in 1) when the work is defined and 2) when and where the work is executed</p>
<h3 id="futures">Futures</h3>
<p>When we create a future in Clojure, we specify the work to be done, and the value returned is a reference to the result (which may not be calculated). The work is immediately scheduled for execution on a separate thread.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>;; Starts on separate thread
user=> (def f (future (Thread/sleep 5000) (println "doing work") "result"))
</code></pre></div></div>
<p>To synchronize the task with the main program flow, we <em>dereference</em> the future. The result may not yet be evaluated, in which case the main program flow blocks until it is.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>;; Blocks until result is available
user=> (deref f)
"doing work"
"result"
user=> @f
"result"
</code></pre></div></div>
<h3 id="promises">Promises</h3>
<p>A promise is very similar to a future in that, it represents a ‘yet-to-be-calculated’ value, and we can synchronize the promise with our main program flow by dereferencing it.</p>
<p>The main difference between promises and futures in Clojure is that a promise doesn’t specify the work that needs to be done. Instead, what’s returned is a container that we can fill at a separate time (known as <em>delivering</em> the promise), and dereference when we want to synchronize with our main program flow.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>user=> (def p (promise))
;; We must deliver the promise before we dereference it,
;; otherwise our program will wait forever
user=> (deliver p "result")
user=> (deref p)
"result"
</code></pre></div></div>
<h3 id="delay">Delay</h3>
<p>Delay is a special form in Clojure that allows us to define work to be done without executing it immediately. Like futures and promises, it returns a reference to a ‘yet-to-be-evaluated’ value. When we need the work performed, we can initiate execution using a construct called <code class="highlighter-rouge">force.</code></p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>user=> (def d (delay (println "doing work") "result"))
user=> (force d)
"doing work"
"result"
</code></pre></div></div>
<p>The ability to define a task without executing it immediately is how we are able to create streams. It’s also useful for creating an object that evaluates only once, since the result is cached.</p>Whether we are aware of it or not, we are intuitively familiar with the concept of concurrency. If we have ever followed a recipe, we know that when we perform a task, there’s often some freedom in how we structure the steps. If a recipe asks us to chop onions, carrots, and peppers, we know that we don’t have to chop them exactly in this order - we can choose to chop the peppers first, or alternate between all three, if we so choose. And this is the very definition of concurrency - the ability to perform certain steps of a task in non-sequential order.Cellular Automata: an overview2017-10-23T18:48:36+00:002017-10-23T18:48:36+00:00/2017/10/23/cellular-automata<p>A cellular automaton is essentially a grid of cells, each of which can be in certain state. The cells evolve step-by-step according to fixed rules, which determine the new state of the cell based on its previous state and its neighbors’ previous states.</p>
<p>For example, we could have two states (“on” and “off”), and a rule that a cell is “on” if its above neighbor was “on” in its previous step, and “off” otherwise:</p>
<p><img src="/assets/cellular_automata_1.jpg" alt="alt text" />
<sub>A trivial example</sub></p>
<p>It is not unlike a recursive function call where the previous output is used as the next input. However there are a few characteristics that make it interesting to study: firstly, it is inherently visual. Next, the modification of cells’ state depends only on adjacent cells, which makes cellular automata a candidate for modeling physical systems.</p>
<h2 id="features">Features</h2>
<p>This seems at first to be a very basic idea. But because the outputs are visual, they lend themselves to empirical study, and one begins to notice all sorts of interesting behavior.</p>
<p>Consider these examples:</p>
<p><img src="/assets/cellular_automata_2.jpg" alt="alt text" />
<sub>Wolfram, <em>A New Kind of Science</em>, p. 25</sub>
<img src="/assets/cellular_automata_3.jpg" alt="alt text" />
<sub>Wolfram, p. 27</sub></p>
<p>One observation from the latter picture (also repeated in the picture’s footnote) is that a very simple rule can lead to a very complex and unpredictable outcome. While anyone who has programmed computers understands this - programs often behave in ways we don’t expect - here is a very clear and visible example.</p>
<p>Another interesting fact is that it seems impossible to predict the outcome of this rule mathematically. One actually has to run the program to see the result. This is an idea known as <em>computational irreducibility</em> - that for certain problems, one can only calculate the output of the program by actually running the computation. (See further reading, for more information).</p>
<h2 id="simple-operations">Simple operations</h2>
<p>Cellular automata are not only a source of interesting pictures and patterns, they can be constructed to perform actual operations as well. Consider this automata:</p>
<p><img src="/assets/cellular_automata_4.jpg" alt="alt text" />
<sub>Wolfram, p. 638</sub>
<img src="/assets/cellular_automata_5.jpg" alt="alt text" />
<sub>Wolfram, p. 662</sub></p>
<p>The second automata, while having more than two states, can perform a logical operation, in this case OR. Beyond these examples, automata have been found that perform other well-known operations, and there are even automata which are universal, which means they can perform any operation which is expressible as a computation.</p>
<h2 id="further-reading">Further reading</h2>
<p>An in-depth treatment of this subject (and the source of these pictures) is Wolfram’s <em>A New Kind of Science.</em></p>A cellular automaton is essentially a grid of cells, each of which can be in certain state. The cells evolve step-by-step according to fixed rules, which determine the new state of the cell based on its previous state and its neighbors’ previous states.Concept and Formalism in Mathematics2017-10-17T18:48:36+00:002017-10-17T18:48:36+00:00/2017/10/17/concept-formalism-mathematics<p>I recently had a discussion around why only certain concepts are considered “mathematical”. For example, why is “number” considered mathematical and “color” (in the everyday sense we understand it), not? A mathematician friend shared the very insightful idea that, at the foundation of our mathematics, we require formalisms that are intertwined with concept. It led me to question some very fundamental ideas about mathematics.</p>
<h2 id="concept">Concept</h2>
<p>First, I thought, what is a concept? A rough definition might be that it is an idea about the essential quality of an object or set of objects. For example, consider a bowl of fruit. This is a collection of objects that has a number of essential qualities that we might agree on: color, ripeness, shape. It also might have qualities that are considered more subjective, such as how appetizing the fruit is. One thing we are very likely to agree on, though, is how many fruit are in the bowl. Here we perceive a fundamental mathematical concept - that of “number.” And this concept is not unique to this bowl of fruit: we might find that the number of fruit in the bowl is coincidentally the same as the number of coins we have in our pocket, in which case these two radically different collections, otherwise incomparable, contain this shared quality, which is quite remarkable.</p>
<p>This is very interesting to think about in its own right. Not only is the “number” shared across different collections, but it seems almost everyone will agree about this quality. It also requires intermediary concepts that are even more universal, like “collection” and “object” and “quantity.” The fact that we all perceive these concepts reveals something deeply shared by us as humans and how we perceive the world.</p>
<h2 id="label">Label</h2>
<p>But concept alone is not enough for mathematics. To begin with, in order to communicate this idea to others, or to arrange it among other ideas in our mind, we must label the concept. So we must associate it with a symbol, for example the word, “number” or “color.” But even a label is not enough for mathematics, it seems - you and I may agree that certain objects have the quality “green” and even may make statements about “green” that we agree upon, such as the fact that green has an earthy quality. But we cannot build a system of mathematics around this, at least in the way we understand modern mathematics.</p>
<h2 id="formalism">Formalism</h2>
<p>Why not? Firstly, mathematics is a science and science must be rigorous. And perhaps because mathematics deals with concept and not the physical, it is extremely subtle and requires the utmost rigor. One cannot get very far in mathematics without being precise - for example, I can have the concepts of “round things” and “pointy things” but I am very limited in the statements I can make from these. It is a very crude mathematics if one wants to consider it mathematics at all.</p>
<p>But if I distill these ideas into the more precise formulations of “circle” and “triangle,” I have a much stronger foundation upon which I can build a powerful mathematical system. Or perhaps another analogy is that precise definitions allow us to inspect and traverse the nuanced world of concept at a much more detailed level. Indeed, because mathematics deals with the conceptual, one requires and can achieve a level of precision that cannot be found in the physical world.</p>
<p>So, in order to get anywhere in mathematics, we require formalizations, which is essentially the result of distilling ideas to a very precise conceptual essence. This is not to say that all of mathematics is formal - in fact the process of mathematics is very creative, informal, and experiential, and often devoid of concept or labeling. But in order to progress in mathematics, individually or collaboratively, the result of our work must be formalized.</p>I recently had a discussion around why only certain concepts are considered “mathematical”. For example, why is “number” considered mathematical and “color” (in the everyday sense we understand it), not? A mathematician friend shared the very insightful idea that, at the foundation of our mathematics, we require formalisms that are intertwined with concept. It led me to question some very fundamental ideas about mathematics.Plants I2017-10-12T17:14:36+00:002017-10-12T17:14:36+00:00/2017/10/12/plants-i<html>
<body>
<script type="text/javascript" src="/assets/plants_1.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/tween.js/17.1.1/Tween.min.js"></script>
<div id="container"></div>
</body>
</html>Mathematics, science, engineering2017-10-03T16:16:36+00:002017-10-03T16:16:36+00:00/2017/10/03/mathematics-science-engineering<h3 id="how-are-these-fields-related">How are these fields related?</h3>
<p>I have been fortunate to have had exposure to these fields - to mathematics as a student, and to engineering through a career stint as a software developer. Though I have not been seriously involved with science, I’ve naturally had exposure through the other disciplines. Despite working in these fields for many years, only recently have I thought more about the <strong>relationship between them</strong>, and perhaps it might be useful to share my line of thought.</p>
<p>To discern the relationship between these fields, the first thing to consider is that each of these fields is related to the world in some way. By world I primarily mean the physical world (as opposed to, say, the conceptual world, which would be another interesting discussion). So a good starting point might be to better understand each field’s relationship to the world.</p>
<h3 id="science">Science</h3>
<p>The goal of science primarily seems to be to <strong>explain</strong> the world. That is, to give reasons as to why certain phenomena occur the way they do. To give reasons for phenomena, one must be able to describe those phenomena, so description and classification must be part of science as well. Also we must note that the value of science is its ability to generalize - if each phenomenon were to have its own unique explanation it would not be much more interesting than having no explanation at all. So science develops principles that apply to general cases, and the corollary of this is another valuable feature of science: the capability to predict phenomena.</p>
<h3 id="engineering">Engineering</h3>
<p>What, then, is engineering’s relationship to the world? It seems that while science aims to explain the world, the primary goal of engineering is to <strong>manipulate</strong> it. The first thing to note is that, in order to manipulate the world, it is easier to do so if one understands something about it. If I am to build some structure made of stone, it would be helpful to know properties of various stone types, e.g. type A is more durable than type B. So here we see a fundamental connection, that science enables engineering. But one quickly perceives this relationship to go both ways - if we improve our ability to manipulate the world, we increase our ability to observe phenomena and explain them.</p>
<p>Also worth mentioning is that to manipulate the world one may require more than just material - also important are tools, people and organizations, or processes, and science can be involved in all facets. But explanations provided by science are never entirely complete (and often up for debate), and engineering bridges the gap between explanation and action.</p>
<h3 id="mathematics">Mathematics</h3>
<p>Finally we come to mathematics’ relationship to the world. One aspect of mathematics is providing formalisms to help <strong>describe</strong> the world and to communicate ideas. (One could also use the words “measure” or “quantify” instead of describe). If I want to describe a phenomenon like atmospheric temperature or orbits of planets, it is helpful if I have the concepts of “number” or “ellipse.” The relationship can also be mysteriously deeper than anticipated - if mathematics provides formalisms which describe the world, and establishes relationships between those formalisms, sometimes those relationships help explain phenomenon in the natural world as well. It also goes both ways, and study of the natural world can expand mathematics - a notable example being the development of calculus as a result of investigations into physics.</p>
<p>Of course almost every mathematician (and myself) would argue that the field is much broader than this, and has a deep and interesting relationship with the conceptual world, but for the purposes of our discussion we won’t delve into that.</p>
<h3 id="connectedness">Connectedness</h3>
<p>So by considering these fields’ relationship to the world, we have begun to perceive certain qualities - those of <strong>description, explanation, and manipulation.</strong> What’s interesting is that these features seem to be intertwined in solving any problem, or perhaps in any human endeavor. To solve a problem, it is best to define the problem, to understand the problem, and then to apply that knowledge in order to skillfully solve the problem. And while this categorization seems formal and distinct, perhaps that need not be the case - in art (or any activity), one must have some idea to express, an understanding of how to express and a means to express it, but one might do these things simultaneously, or without differentiating between them.</p>How are these fields related?Thinking through minimax step-by-step2017-08-16T17:14:36+00:002017-08-16T17:14:36+00:00/2017/08/16/minimax-step-by-step<p>I’d like to write about my experience learning and implementing minimax for the first time.</p>
<p>Perhaps the line of thought that I followed might help others in understanding the concept, specifically as it relates to implementing an algorithm for tic-tac-toe.</p>
<p>Disclaimer: I’m not familiar with the details of economics or game theory. I may be using terms loosely. If I am using a term in a way that’s grossly inaccurate or can be improved, please let me know.</p>
<h2 id="step-1-become-familiar-with-the-concept-of-minimax">Step 1: Become familiar with the concept of minimax</h2>
<p>The best place to start is the <a href="https://en.wikipedia.org/wiki/Minimax">wikipedia article</a>.</p>
<p>The fundamental idea is that, when making a decision in a “game” (i.e. a situation with multiple players that entails everyone making rational choices), we would like to pursue a strategy that <strong><em>min</em></strong><strong>imizes our</strong> <strong><em>max</em></strong><strong>imum loss.</strong> So, if I were to choose between two options: one, where I could win 50 but also lose 30, and another where I could win 20 or also lose 20, I would choose the latter. It is a “play-not-to-lose” strategy:</p>
<p>We could contrast this with other kinds of strategies: perhaps a “maximax” strategy where we <em>max</em>imize our <em>max</em>imum gain, in which case we would choose the option where we might win 50. But we can also see why a strategy like this doesn’t make much sense in a zero-sum game like tic-tac-toe: our opponents are going to make decisions that move them towards their maximum gain, which is equivalent to our maximum loss.</p>
<h2 id="step-2-explore-minimax-for-multi-step-games">Step 2: Explore minimax for multi-step games</h2>
<p>Given a situation where we make a single decision, and our opponent makes a single decision, the concept of minimax is hopefully clearer. But how do we apply this concept to a game like tic-tac-toe, that can have many future states, and has an opponent who will make a series of moves we can’t predict?</p>
<p>Or, put another way: when it is our turn, we will have a set of available moves we can make. If we want to choose the move that minimizes our maximum loss, how do we quantify “maximum loss” for each of these moves?</p>
<h3 id="finding-a-valuation-function-for-maximum-loss">Finding a valuation function for “maximum loss”</h3>
<p><img src="/assets/minimax_example_1.jpg" alt="alt text" />
<sub>Example 1: 3 moves left</sub></p>
<p>Consider the above example. In this example, player “X” can choose from 3 possible moves. We need some way to quantify the maximum loss for each of these moves. Put it another way, we need some sort of “valuation function” that returns a maximum loss for each option.</p>
<h4 id="solution-1-worst-possible-case">Solution 1: Worst possible case</h4>
<p>The first thing I considered here was, what if we assumed that subsequent decisions are arbitrary, and the maximum loss was just the worst possible scenario? Using the above example, if we choose (1), the worst possible case is we lose. Same if we choose option (3). But if we choose option (9), then the worst case is a tie. Let’s choose to assign numerical weights to these scenarios: value a loss as a loss of +∞, value a tie as a loss of 0, and value a win as a loss of -∞</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Valuation (using worst-case):
1) We lose, value at +∞
3) We lose, value at +∞
9) We tie, value at 0.
</code></pre></div></div>
<p>Applying the concept of minimax here, the minimum of our “maximum losses” is the minimum of (+∞, +∞, 0), which is 0, move 9.</p>
<h4 id="solution-2-likelihood-of-loss">Solution 2: Likelihood of loss</h4>
<p>Is this approach sufficient? Consider this next example. Now I must admit it is a fake game that doesn’t really make sense according to the traditional rules of tic-tac-toe, but will help illustrate some concepts:</p>
<p><img src="/assets/minimax_example_2.jpg" alt="alt text" />
<sub>Example 2: 4 moves left. “X” indicates X wins, same for “O”. “C” indicates cats game.</sub></p>
<p>Here, X can choose from 4 possible moves: 1, 2, 3 and 9. Let’s focus on the moves 1 and 2, and ignore moves 3 and 9 for now. Let’s assume all subsequent moves are random. If we use the valuation function described above, where the “maximum loss” for each move is just the worst possible scenario, then (1) and (2) should be valued equally - both could potentially lead to a loss for X. So according to our previous idea of valuation, either choice is equally good. This is clearly inaccurate - it would be best to choose (2), because if subsquent moves were random, X has a 50% chance of winning in that case, whereas it only has a 17% chance of winning for choice (1).</p>
<p>So then is the right method of valuing each choice a function of the % of possible end states that lead to a win? That certainly seems reasonable.</p>
<h4 id="solution-3-assume-we-are-rational">Solution 3: Assume we are rational</h4>
<p><img src="/assets/minimax_example_3.jpg" alt="alt text" />
<sub>Example 3</sub></p>
<p>Let’s extend our previous example by considering move (9) in addition to move (2). By our new decision making process, between choice (2) and choice (9), choice 9 is superior: 4/6 end states lead to a win for X, while for choice (2), only 3/6 end states lead to a win for X. This holds if all players make arbitrary decisions. But if we assume others (and our) future decisions to be rational, it becomes clearer that option (2) is a better choice. For, no matter what “O” chooses after X moves to (2), X can then force him to ultimately lose. So our valuation function must be improved further yet to account for our ability to make rational decisions.</p>
<p>What would that function look like? It could be something along the lines of, “if we were to behave rationally for all of our subsequent turns, what is the maximum loss we could expect?”<sup>1</sup> In the case of example 3, choice (2), the maximum loss or worst case is that we win, i.e. a loss of -∞. There is no case where we lose or tie. In example 3 choice (9), the maximum loss is the case where we lose, or a loss of +∞. (We could just as well use -1, 1 instead of -∞, +∞). Since we want to <em>minimize</em> our <em>maximum loss</em>, clearly the minimum is -∞, or choice 2.</p>
<hr />
<p><sup>1</sup> Rationally, in our case, means minimax. So the solution is recursive!</p>
<h2 id="step-3-implementing-an-algorithm-for-tic-tac-toe">Step 3: Implementing an algorithm for tic-tac-toe</h2>
<p>How can we implement this idea in tic-tac-toe? Assume it is our turn (we being the computer), and we have a given board. Say there are 3 moves we can make. Assume we had a valuation function that calculated the maximum loss for each of these three moves. Then our algorithm should calculate the maximum loss for each of these moves, and return the move that corresponds to the minimum of these maximum losses.</p>
<p>At this point, it will be helpful to define some terms:</p>
<ul>
<li><strong>s</strong>: a game state where it is our turn</li>
<li><strong>ŝ</strong>: a game state where it is the opposing players turn</li>
<li><strong>max-loss(s)/max-loss(ŝ)</strong>: a function that takes in a given state, and returns the maximum loss</li>
</ul>
<p>So, repeating our above example using these terms, we are given a state <strong>s</strong>. Since we have three moves, we have the option of moving the game into 3 different states: <strong>ŝ₁, ŝ₂, ŝ₃</strong>. We want to calculate the values <strong>max-loss(ŝ₁), max-loss(ŝ₂), and max-loss(ŝ₃)</strong> and return the move that corresponds to the minimum of these values</p>
<h3 id="writing-the-max-loss-function-for-ŝ-opposing-players-turn">Writing the max-loss function for <em>ŝ</em> (opposing player’s turn)</h3>
<p>How do we write a function that calculates <strong>max-loss(ŝ)</strong>?</p>
<p>If <strong>ŝ</strong> is a leaf node (i.e. the game is over), we can define the return values as follows:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>max-loss(ŝ), where ŝ is a leaf node:
= 1, if opposing player wins
= 0, if draw
= -1, if I win.
</code></pre></div></div>
<p>Here we are using 1, -1 instead of +∞, -∞. Also notice that when we calculate the loss, we are <strong>always</strong> doing it from <strong>our</strong> perspective. So if the opposing player wins, that’s a <strong>loss</strong> of <strong>+1</strong>. Whereas if I win, it’s a <strong>loss</strong> of <strong>-1</strong>.</p>
<p>What if <strong>ŝ</strong> is not a leaf node? Since it’s the opposing player’s turn, and we don’t know their moves in advance, we need to account for the possibility that they will choose the worst for us, i.e. the maximum loss for us out of all their options. So we can define max-loss as follows:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>max-loss(ŝ), where ŝ is not a leaf node:
= maximum(max-loss(s₁), max-loss(s₂) ... max-loss(sᵢ))
(where {sᵢ} is the set of states the opposing player can move to)
</code></pre></div></div>
<p>Now we must define what we mean by <strong>max-loss(s)</strong>, i.e. calculating the max-loss when it is our turn.</p>
<h3 id="writing-the-max-loss-function-for-s-our-turn">Writing the max-loss function for <em>s</em> (our turn)</h3>
<p>Now we must define <strong>max-loss(s)</strong>, i.e. a state where it is our turn. If <strong>s</strong> is a leaf node, we can use the exact same definition as before:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>max-loss(s), where s is a leaf node:
= 1, if opposing player wins
= 0, if draw
= -1, if I win.
</code></pre></div></div>
<p>However, if <strong>s</strong> is not a leaf node, then we should assume we will make an intelligent choice (i.e. choose the option that minimizes our max loss). Thus:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>max-loss(s), where s is not a leaf node:
= minimum(max-loss(ŝ₁), max-loss(ŝ₂) ... max-loss(ŝᵢ))
(where {ŝᵢ} is the set of states we can choose to move to)
</code></pre></div></div>
<p>This gives us a complete definition of the <strong>max-loss</strong> function. Now what remains is a top-level function that gives us the optimal move.</p>
<h3 id="writing-the-get-move-function">Writing the get-move function</h3>
<p>This is similar to the max-loss function, except instead of returning a max-loss value, we return the move. Let’s define the function, <strong>get-move(s)</strong> which takes in a game state where it’s our turn, and returns a move. We already outlined an algorithm for this above.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>get-move(s), where s is never a leaf node:
- assume, given s, we can make moves 1, 2 ... i
- These correspond to potential game states ŝ₁, ŝ₂ ... ŝᵢ
- calculate max-loss(ŝ₁), max-loss(ŝ₂) ... max-loss(ŝᵢ)
- return the move that has the minimum max-loss
</code></pre></div></div>
<h3 id="example">Example</h3>
<p>The <code class="highlighter-rouge">max-loss</code> and <code class="highlighter-rouge">get-move</code> functions are all we need to implement minimax.</p>
<p>If you would like to see a working version in clojure, you can see one here: <a href="https://github.com/whoisutsav/tic-tac-toe">https://github.com/whoisutsav/tic-tac-toe</a></p>I’d like to write about my experience learning and implementing minimax for the first time.Tetrahedron2017-07-23T03:14:36+00:002017-07-23T03:14:36+00:00/2017/07/22/tetrahedron<html>
<body>
<script type="text/javascript" src="/assets/tetrahedron.js"></script>
<div id="container"></div>
</body>
</html>Follow2017-07-18T19:44:36+00:002017-07-18T19:44:36+00:00/2017/07/18/follow<html>
<body>
<script type="text/javascript" src="/assets/follow.js"></script>
<div id="container"></div>
</body>
</html>Electricity III2017-07-18T17:57:36+00:002017-07-18T17:57:36+00:00/2017/07/18/electricity-3<html>
<body>
<script type="text/javascript" src="/assets/electricity_3.js"></script>
<div id="container"></div>
</body>
</html>