Browse Source

automated update from build.py

main
Colin McMillen 3 years ago
parent
commit
06d911d482
  1. 124
      blog/20070502-robot-behaviors-python.html
  2. 126
      feed.atom

124
blog/20070502-robot-behaviors-python.html

@ -42,91 +42,91 @@
</ol>
<p>The usual approach to robot behavior design relies on hierarchical state machines. Specifically, we might be in a &ldquo;standing&rdquo; state while the ball is far away; when the ball becomes close, we enter a &ldquo;diving&rdquo; state that persists for one second. Because of requirement 3, this solution will have a few warts: we need to keep track of how much time we&rsquo;ve spent in the dive state. Every time we add a special case like this, we need to keep some extra state information around. Since robotics code is full of special cases, we tend to end up with a lot of bookkeeping cruft. In contrast, generators will let us clearly express the desired behavior.</p>
<p>On to the state-machine approach. First, we&rsquo;ll have a class called Features that abstracts the robot&rsquo;s raw sensor data. For this example, we only care whether the ball is near/far and left/right, so Features will just contain two boolean variables:</p>
<div class="codehilite"><pre><span></span> <span class="k">class</span> <span class="nc">Features</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="n">ballFar</span> <span class="o">=</span> <span class="bp">True</span>
<span class="n">ballOnLeft</span> <span class="o">=</span> <span class="bp">True</span>
<div class="codehilite"><pre><span></span><span class="k">class</span> <span class="nc">Features</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="n">ballFar</span> <span class="o">=</span> <span class="bp">True</span>
<span class="n">ballOnLeft</span> <span class="o">=</span> <span class="bp">True</span>
</pre></div>
<p>Next, we make the goalkeeper. The keeper&rsquo;s behavior is specified by the <code>next()</code> function, which is called thirty times per second by the robot&rsquo;s main event loop (every time the on-board camera produces a new image). The <code>next()</code> function returns one of three actions: <code>"stand"</code>, <code>"diveLeft"</code>, or <code>"diveRight"</code>, based on the current values of the Features object. For now, let&rsquo;s pretend that requirement 3 doesn&rsquo;t exist.</p>
<div class="codehilite"><pre><span></span> <span class="k">class</span> <span class="nc">Goalkeeper</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">features</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">features</span> <span class="o">=</span> <span class="n">features</span>
<div class="codehilite"><pre><span></span><span class="k">class</span> <span class="nc">Goalkeeper</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">features</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">features</span> <span class="o">=</span> <span class="n">features</span>
<span class="k">def</span> <span class="nf">next</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">features</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">features</span>
<span class="k">if</span> <span class="n">features</span><span class="o">.</span><span class="n">ballFar</span><span class="p">:</span>
<span class="k">return</span> <span class="s1">&#39;stand&#39;</span>
<span class="k">def</span> <span class="nf">next</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">features</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">features</span>
<span class="k">if</span> <span class="n">features</span><span class="o">.</span><span class="n">ballFar</span><span class="p">:</span>
<span class="k">return</span> <span class="s1">&#39;stand&#39;</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">if</span> <span class="n">features</span><span class="o">.</span><span class="n">ballOnLeft</span><span class="p">:</span>
<span class="k">return</span> <span class="s1">&#39;diveLeft&#39;</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">if</span> <span class="n">features</span><span class="o">.</span><span class="n">ballOnLeft</span><span class="p">:</span>
<span class="k">return</span> <span class="s1">&#39;diveLeft&#39;</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="s1">&#39;diveRight&#39;</span>
<span class="k">return</span> <span class="s1">&#39;diveRight&#39;</span>
</pre></div>
<p>That was simple enough. The constructor takes in the <code>Features</code> object; the <code>next()</code> method checks the current <code>Features</code> values and returns the correct action. Now, how about satisfying requirement 3? When we choose to dive, we need to keep track of two things: how long we need to stay in the <code>"dive"</code> state and which direction we dove. We&rsquo;ll do this by adding a couple of instance variables (<code>self.diveFramesRemaining</code> and <code>self.lastDiveCommand</code>) to the Goalkeeper class. These variables are set when we initiate the dive. At the top of the <code>next()</code> function, we check if <code>self.diveFramesRemaining</code> is positive; if so, we can immediately return <code>self.lastDiveCommand</code> without consulting the <code>Features</code>. Here&rsquo;s the code:</p>
<div class="codehilite"><pre><span></span> <span class="k">class</span> <span class="nc">Goalkeeper</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">features</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">features</span> <span class="o">=</span> <span class="n">features</span>
<span class="bp">self</span><span class="o">.</span><span class="n">diveFramesRemaining</span> <span class="o">=</span> <span class="mi">0</span>
<span class="bp">self</span><span class="o">.</span><span class="n">lastDiveCommand</span> <span class="o">=</span> <span class="bp">None</span>
<div class="codehilite"><pre><span></span><span class="k">class</span> <span class="nc">Goalkeeper</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">features</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">features</span> <span class="o">=</span> <span class="n">features</span>
<span class="bp">self</span><span class="o">.</span><span class="n">diveFramesRemaining</span> <span class="o">=</span> <span class="mi">0</span>
<span class="bp">self</span><span class="o">.</span><span class="n">lastDiveCommand</span> <span class="o">=</span> <span class="bp">None</span>
<span class="k">def</span> <span class="nf">next</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">features</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">features</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">diveFramesRemaining</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">diveFramesRemaining</span> <span class="o">-=</span> <span class="mi">1</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">lastDiveCommand</span>
<span class="k">def</span> <span class="nf">next</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">features</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">features</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">diveFramesRemaining</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">diveFramesRemaining</span> <span class="o">-=</span> <span class="mi">1</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">lastDiveCommand</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">if</span> <span class="n">features</span><span class="o">.</span><span class="n">ballFar</span><span class="p">:</span>
<span class="k">return</span> <span class="s1">&#39;stand&#39;</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">if</span> <span class="n">features</span><span class="o">.</span><span class="n">ballFar</span><span class="p">:</span>
<span class="k">return</span> <span class="s1">&#39;stand&#39;</span>
<span class="k">if</span> <span class="n">features</span><span class="o">.</span><span class="n">ballOnLeft</span><span class="p">:</span>
<span class="n">command</span> <span class="o">=</span> <span class="s1">&#39;diveLeft&#39;</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">if</span> <span class="n">features</span><span class="o">.</span><span class="n">ballOnLeft</span><span class="p">:</span>
<span class="n">command</span> <span class="o">=</span> <span class="s1">&#39;diveLeft&#39;</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">command</span> <span class="o">=</span> <span class="s1">&#39;diveRight&#39;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">lastDiveCommand</span> <span class="o">=</span> <span class="n">command</span>
<span class="bp">self</span><span class="o">.</span><span class="n">diveFramesRemaining</span> <span class="o">=</span> <span class="mi">29</span>
<span class="k">return</span> <span class="n">command</span>
<span class="n">command</span> <span class="o">=</span> <span class="s1">&#39;diveRight&#39;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">lastDiveCommand</span> <span class="o">=</span> <span class="n">command</span>
<span class="bp">self</span><span class="o">.</span><span class="n">diveFramesRemaining</span> <span class="o">=</span> <span class="mi">29</span>
<span class="k">return</span> <span class="n">command</span>
</pre></div>
<p>This satisfies all the requirements, but it&rsquo;s ugly. We&rsquo;ve added a couple of bookkeeping variables to the Goalkeeper class. Code to properly maintain these variables is sprinkled all over the <code>next()</code> function. Even worse, the structure of the code no longer accurately represents the programmer&rsquo;s intent: the top-level if-statement depends on the state of the robot rather than the state of the world. The intent of the original <code>next()</code> function is much easier to discern. (In real code, we could use a state-machine class to tidy things up a bit, but the end result would still be ugly when compared to our original <code>next()</code> function.)</p>
<p>With generators, we can preserve the form of the original <code>next()</code> function and keep the bookkeeping only where it&rsquo;s needed. If you&rsquo;re not familiar with generators, you can think of them as a special kind of function. The <code>yield</code> keyword is essentially equivalent to <code>return</code>, but the next time the generator is called, <em>execution continues from the point of the last <code>yield</code></em>, preserving the state of all local variables. With <code>yield</code>, we can use a <code>for</code> loop to &ldquo;return&rdquo; the same dive command the next 30 times the function is called! Lines 11-16 of the below code show the magic:</p>
<div class="codehilite"><pre><span></span> <span class="k">class</span> <span class="nc">GoalkeeperWithGenerator</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">features</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">features</span> <span class="o">=</span> <span class="n">features</span>
<span class="k">def</span> <span class="nf">behavior</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="n">features</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">features</span>
<span class="k">if</span> <span class="n">features</span><span class="o">.</span><span class="n">ballFar</span><span class="p">:</span>
<span class="k">yield</span> <span class="s1">&#39;stand&#39;</span>
<div class="codehilite"><pre><span></span><span class="k">class</span> <span class="nc">GoalkeeperWithGenerator</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">features</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">features</span> <span class="o">=</span> <span class="n">features</span>
<span class="k">def</span> <span class="nf">behavior</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="n">features</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">features</span>
<span class="k">if</span> <span class="n">features</span><span class="o">.</span><span class="n">ballFar</span><span class="p">:</span>
<span class="k">yield</span> <span class="s1">&#39;stand&#39;</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">if</span> <span class="n">features</span><span class="o">.</span><span class="n">ballOnLeft</span><span class="p">:</span>
<span class="n">command</span> <span class="o">=</span> <span class="s1">&#39;diveLeft&#39;</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">if</span> <span class="n">features</span><span class="o">.</span><span class="n">ballOnLeft</span><span class="p">:</span>
<span class="n">command</span> <span class="o">=</span> <span class="s1">&#39;diveLeft&#39;</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">command</span> <span class="o">=</span> <span class="s1">&#39;diveRight&#39;</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">xrange</span><span class="p">(</span><span class="mi">30</span><span class="p">):</span>
<span class="k">yield</span> <span class="n">command</span>
<span class="n">command</span> <span class="o">=</span> <span class="s1">&#39;diveRight&#39;</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">xrange</span><span class="p">(</span><span class="mi">30</span><span class="p">):</span>
<span class="k">yield</span> <span class="n">command</span>
</pre></div>
<p>Here&rsquo;s a simple driver script that shows how to use our goalkeepers:</p>
<div class="codehilite"><pre><span></span> <span class="kn">import</span> <span class="nn">random</span>
<span class="n">f</span> <span class="o">=</span> <span class="n">Features</span><span class="p">()</span>
<span class="n">g1</span> <span class="o">=</span> <span class="n">Goalkeeper</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="n">g2</span> <span class="o">=</span> <span class="n">GoalkeeperWithGenerator</span><span class="p">(</span><span class="n">f</span><span class="p">)</span><span class="o">.</span><span class="n">behavior</span><span class="p">()</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">xrange</span><span class="p">(</span><span class="mi">10000</span><span class="p">):</span>
<span class="n">f</span><span class="o">.</span><span class="n">ballFar</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">random</span><span class="p">()</span> <span class="o">&gt;</span> <span class="mf">0.1</span>
<span class="n">f</span><span class="o">.</span><span class="n">ballOnLeft</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">random</span><span class="p">()</span> <span class="o">&lt;</span> <span class="mf">0.5</span>
<span class="n">g1action</span> <span class="o">=</span> <span class="n">g1</span><span class="o">.</span><span class="n">next</span><span class="p">()</span>
<span class="n">g2action</span> <span class="o">=</span> <span class="n">g2</span><span class="o">.</span><span class="n">next</span><span class="p">()</span>
<span class="k">print</span> <span class="s2">&quot;</span><span class="si">%s</span><span class="se">\t</span><span class="si">%s</span><span class="se">\t</span><span class="si">%s</span><span class="se">\t</span><span class="si">%s</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="p">(</span>
<span class="n">f</span><span class="o">.</span><span class="n">ballFar</span><span class="p">,</span> <span class="n">f</span><span class="o">.</span><span class="n">ballOnLeft</span><span class="p">,</span> <span class="n">g1action</span><span class="p">,</span> <span class="n">g2action</span><span class="p">)</span>
<span class="k">assert</span><span class="p">(</span><span class="n">g1action</span> <span class="o">==</span> <span class="n">g2action</span><span class="p">)</span>
<div class="codehilite"><pre><span></span><span class="kn">import</span> <span class="nn">random</span>
<span class="n">f</span> <span class="o">=</span> <span class="n">Features</span><span class="p">()</span>
<span class="n">g1</span> <span class="o">=</span> <span class="n">Goalkeeper</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="n">g2</span> <span class="o">=</span> <span class="n">GoalkeeperWithGenerator</span><span class="p">(</span><span class="n">f</span><span class="p">)</span><span class="o">.</span><span class="n">behavior</span><span class="p">()</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">xrange</span><span class="p">(</span><span class="mi">10000</span><span class="p">):</span>
<span class="n">f</span><span class="o">.</span><span class="n">ballFar</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">random</span><span class="p">()</span> <span class="o">&gt;</span> <span class="mf">0.1</span>
<span class="n">f</span><span class="o">.</span><span class="n">ballOnLeft</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">random</span><span class="p">()</span> <span class="o">&lt;</span> <span class="mf">0.5</span>
<span class="n">g1action</span> <span class="o">=</span> <span class="n">g1</span><span class="o">.</span><span class="n">next</span><span class="p">()</span>
<span class="n">g2action</span> <span class="o">=</span> <span class="n">g2</span><span class="o">.</span><span class="n">next</span><span class="p">()</span>
<span class="k">print</span> <span class="s2">&quot;</span><span class="si">%s</span><span class="se">\t</span><span class="si">%s</span><span class="se">\t</span><span class="si">%s</span><span class="se">\t</span><span class="si">%s</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="p">(</span>
<span class="n">f</span><span class="o">.</span><span class="n">ballFar</span><span class="p">,</span> <span class="n">f</span><span class="o">.</span><span class="n">ballOnLeft</span><span class="p">,</span> <span class="n">g1action</span><span class="p">,</span> <span class="n">g2action</span><span class="p">)</span>
<span class="k">assert</span><span class="p">(</span><span class="n">g1action</span> <span class="o">==</span> <span class="n">g2action</span><span class="p">)</span>
</pre></div>

126
feed.atom

@ -4,7 +4,7 @@
<title>Colin McMillen's Blog</title>
<link href="https://www.mcmillen.dev"/>
<link rel="self" href="https://www.mcmillen.dev/feed.atom"/>
<updated>2021-07-01T20:10:09-04:00</updated>
<updated>2021-07-01T20:13:55-04:00</updated>
<author>
<name>Colin McMillen</name>
</author>
@ -29,91 +29,91 @@
</ol>
<p>The usual approach to robot behavior design relies on hierarchical state machines. Specifically, we might be in a &ldquo;standing&rdquo; state while the ball is far away; when the ball becomes close, we enter a &ldquo;diving&rdquo; state that persists for one second. Because of requirement 3, this solution will have a few warts: we need to keep track of how much time we&rsquo;ve spent in the dive state. Every time we add a special case like this, we need to keep some extra state information around. Since robotics code is full of special cases, we tend to end up with a lot of bookkeeping cruft. In contrast, generators will let us clearly express the desired behavior.</p>
<p>On to the state-machine approach. First, we&rsquo;ll have a class called Features that abstracts the robot&rsquo;s raw sensor data. For this example, we only care whether the ball is near/far and left/right, so Features will just contain two boolean variables:</p>
<div class="codehilite"><pre><span></span> <span class="k">class</span> <span class="nc">Features</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="n">ballFar</span> <span class="o">=</span> <span class="bp">True</span>
<span class="n">ballOnLeft</span> <span class="o">=</span> <span class="bp">True</span>
<div class="codehilite"><pre><span></span><span class="k">class</span> <span class="nc">Features</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="n">ballFar</span> <span class="o">=</span> <span class="bp">True</span>
<span class="n">ballOnLeft</span> <span class="o">=</span> <span class="bp">True</span>
</pre></div>
<p>Next, we make the goalkeeper. The keeper&rsquo;s behavior is specified by the <code>next()</code> function, which is called thirty times per second by the robot&rsquo;s main event loop (every time the on-board camera produces a new image). The <code>next()</code> function returns one of three actions: <code>"stand"</code>, <code>"diveLeft"</code>, or <code>"diveRight"</code>, based on the current values of the Features object. For now, let&rsquo;s pretend that requirement 3 doesn&rsquo;t exist.</p>
<div class="codehilite"><pre><span></span> <span class="k">class</span> <span class="nc">Goalkeeper</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">features</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">features</span> <span class="o">=</span> <span class="n">features</span>
<div class="codehilite"><pre><span></span><span class="k">class</span> <span class="nc">Goalkeeper</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">features</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">features</span> <span class="o">=</span> <span class="n">features</span>
<span class="k">def</span> <span class="nf">next</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">features</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">features</span>
<span class="k">if</span> <span class="n">features</span><span class="o">.</span><span class="n">ballFar</span><span class="p">:</span>
<span class="k">return</span> <span class="s1">&#39;stand&#39;</span>
<span class="k">def</span> <span class="nf">next</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">features</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">features</span>
<span class="k">if</span> <span class="n">features</span><span class="o">.</span><span class="n">ballFar</span><span class="p">:</span>
<span class="k">return</span> <span class="s1">&#39;stand&#39;</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">if</span> <span class="n">features</span><span class="o">.</span><span class="n">ballOnLeft</span><span class="p">:</span>
<span class="k">return</span> <span class="s1">&#39;diveLeft&#39;</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">if</span> <span class="n">features</span><span class="o">.</span><span class="n">ballOnLeft</span><span class="p">:</span>
<span class="k">return</span> <span class="s1">&#39;diveLeft&#39;</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="s1">&#39;diveRight&#39;</span>
<span class="k">return</span> <span class="s1">&#39;diveRight&#39;</span>
</pre></div>
<p>That was simple enough. The constructor takes in the <code>Features</code> object; the <code>next()</code> method checks the current <code>Features</code> values and returns the correct action. Now, how about satisfying requirement 3? When we choose to dive, we need to keep track of two things: how long we need to stay in the <code>"dive"</code> state and which direction we dove. We&rsquo;ll do this by adding a couple of instance variables (<code>self.diveFramesRemaining</code> and <code>self.lastDiveCommand</code>) to the Goalkeeper class. These variables are set when we initiate the dive. At the top of the <code>next()</code> function, we check if <code>self.diveFramesRemaining</code> is positive; if so, we can immediately return <code>self.lastDiveCommand</code> without consulting the <code>Features</code>. Here&rsquo;s the code:</p>
<div class="codehilite"><pre><span></span> <span class="k">class</span> <span class="nc">Goalkeeper</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">features</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">features</span> <span class="o">=</span> <span class="n">features</span>
<span class="bp">self</span><span class="o">.</span><span class="n">diveFramesRemaining</span> <span class="o">=</span> <span class="mi">0</span>
<span class="bp">self</span><span class="o">.</span><span class="n">lastDiveCommand</span> <span class="o">=</span> <span class="bp">None</span>
<div class="codehilite"><pre><span></span><span class="k">class</span> <span class="nc">Goalkeeper</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">features</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">features</span> <span class="o">=</span> <span class="n">features</span>
<span class="bp">self</span><span class="o">.</span><span class="n">diveFramesRemaining</span> <span class="o">=</span> <span class="mi">0</span>
<span class="bp">self</span><span class="o">.</span><span class="n">lastDiveCommand</span> <span class="o">=</span> <span class="bp">None</span>
<span class="k">def</span> <span class="nf">next</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">features</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">features</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">diveFramesRemaining</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">diveFramesRemaining</span> <span class="o">-=</span> <span class="mi">1</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">lastDiveCommand</span>
<span class="k">def</span> <span class="nf">next</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">features</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">features</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">diveFramesRemaining</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">diveFramesRemaining</span> <span class="o">-=</span> <span class="mi">1</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">lastDiveCommand</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">if</span> <span class="n">features</span><span class="o">.</span><span class="n">ballFar</span><span class="p">:</span>
<span class="k">return</span> <span class="s1">&#39;stand&#39;</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">if</span> <span class="n">features</span><span class="o">.</span><span class="n">ballFar</span><span class="p">:</span>
<span class="k">return</span> <span class="s1">&#39;stand&#39;</span>
<span class="k">if</span> <span class="n">features</span><span class="o">.</span><span class="n">ballOnLeft</span><span class="p">:</span>
<span class="n">command</span> <span class="o">=</span> <span class="s1">&#39;diveLeft&#39;</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">if</span> <span class="n">features</span><span class="o">.</span><span class="n">ballOnLeft</span><span class="p">:</span>
<span class="n">command</span> <span class="o">=</span> <span class="s1">&#39;diveLeft&#39;</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">command</span> <span class="o">=</span> <span class="s1">&#39;diveRight&#39;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">lastDiveCommand</span> <span class="o">=</span> <span class="n">command</span>
<span class="bp">self</span><span class="o">.</span><span class="n">diveFramesRemaining</span> <span class="o">=</span> <span class="mi">29</span>
<span class="k">return</span> <span class="n">command</span>
<span class="n">command</span> <span class="o">=</span> <span class="s1">&#39;diveRight&#39;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">lastDiveCommand</span> <span class="o">=</span> <span class="n">command</span>
<span class="bp">self</span><span class="o">.</span><span class="n">diveFramesRemaining</span> <span class="o">=</span> <span class="mi">29</span>
<span class="k">return</span> <span class="n">command</span>
</pre></div>
<p>This satisfies all the requirements, but it&rsquo;s ugly. We&rsquo;ve added a couple of bookkeeping variables to the Goalkeeper class. Code to properly maintain these variables is sprinkled all over the <code>next()</code> function. Even worse, the structure of the code no longer accurately represents the programmer&rsquo;s intent: the top-level if-statement depends on the state of the robot rather than the state of the world. The intent of the original <code>next()</code> function is much easier to discern. (In real code, we could use a state-machine class to tidy things up a bit, but the end result would still be ugly when compared to our original <code>next()</code> function.)</p>
<p>With generators, we can preserve the form of the original <code>next()</code> function and keep the bookkeeping only where it&rsquo;s needed. If you&rsquo;re not familiar with generators, you can think of them as a special kind of function. The <code>yield</code> keyword is essentially equivalent to <code>return</code>, but the next time the generator is called, <em>execution continues from the point of the last <code>yield</code></em>, preserving the state of all local variables. With <code>yield</code>, we can use a <code>for</code> loop to &ldquo;return&rdquo; the same dive command the next 30 times the function is called! Lines 11-16 of the below code show the magic:</p>
<div class="codehilite"><pre><span></span> <span class="k">class</span> <span class="nc">GoalkeeperWithGenerator</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">features</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">features</span> <span class="o">=</span> <span class="n">features</span>
<span class="k">def</span> <span class="nf">behavior</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="n">features</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">features</span>
<span class="k">if</span> <span class="n">features</span><span class="o">.</span><span class="n">ballFar</span><span class="p">:</span>
<span class="k">yield</span> <span class="s1">&#39;stand&#39;</span>
<div class="codehilite"><pre><span></span><span class="k">class</span> <span class="nc">GoalkeeperWithGenerator</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">features</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">features</span> <span class="o">=</span> <span class="n">features</span>
<span class="k">def</span> <span class="nf">behavior</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="n">features</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">features</span>
<span class="k">if</span> <span class="n">features</span><span class="o">.</span><span class="n">ballFar</span><span class="p">:</span>
<span class="k">yield</span> <span class="s1">&#39;stand&#39;</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">if</span> <span class="n">features</span><span class="o">.</span><span class="n">ballOnLeft</span><span class="p">:</span>
<span class="n">command</span> <span class="o">=</span> <span class="s1">&#39;diveLeft&#39;</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">if</span> <span class="n">features</span><span class="o">.</span><span class="n">ballOnLeft</span><span class="p">:</span>
<span class="n">command</span> <span class="o">=</span> <span class="s1">&#39;diveLeft&#39;</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">command</span> <span class="o">=</span> <span class="s1">&#39;diveRight&#39;</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">xrange</span><span class="p">(</span><span class="mi">30</span><span class="p">):</span>
<span class="k">yield</span> <span class="n">command</span>
<span class="n">command</span> <span class="o">=</span> <span class="s1">&#39;diveRight&#39;</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">xrange</span><span class="p">(</span><span class="mi">30</span><span class="p">):</span>
<span class="k">yield</span> <span class="n">command</span>
</pre></div>
<p>Here&rsquo;s a simple driver script that shows how to use our goalkeepers:</p>
<div class="codehilite"><pre><span></span> <span class="kn">import</span> <span class="nn">random</span>
<span class="n">f</span> <span class="o">=</span> <span class="n">Features</span><span class="p">()</span>
<span class="n">g1</span> <span class="o">=</span> <span class="n">Goalkeeper</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="n">g2</span> <span class="o">=</span> <span class="n">GoalkeeperWithGenerator</span><span class="p">(</span><span class="n">f</span><span class="p">)</span><span class="o">.</span><span class="n">behavior</span><span class="p">()</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">xrange</span><span class="p">(</span><span class="mi">10000</span><span class="p">):</span>
<span class="n">f</span><span class="o">.</span><span class="n">ballFar</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">random</span><span class="p">()</span> <span class="o">&gt;</span> <span class="mf">0.1</span>
<span class="n">f</span><span class="o">.</span><span class="n">ballOnLeft</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">random</span><span class="p">()</span> <span class="o">&lt;</span> <span class="mf">0.5</span>
<span class="n">g1action</span> <span class="o">=</span> <span class="n">g1</span><span class="o">.</span><span class="n">next</span><span class="p">()</span>
<span class="n">g2action</span> <span class="o">=</span> <span class="n">g2</span><span class="o">.</span><span class="n">next</span><span class="p">()</span>
<span class="k">print</span> <span class="s2">&quot;</span><span class="si">%s</span><span class="se">\t</span><span class="si">%s</span><span class="se">\t</span><span class="si">%s</span><span class="se">\t</span><span class="si">%s</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="p">(</span>
<span class="n">f</span><span class="o">.</span><span class="n">ballFar</span><span class="p">,</span> <span class="n">f</span><span class="o">.</span><span class="n">ballOnLeft</span><span class="p">,</span> <span class="n">g1action</span><span class="p">,</span> <span class="n">g2action</span><span class="p">)</span>
<span class="k">assert</span><span class="p">(</span><span class="n">g1action</span> <span class="o">==</span> <span class="n">g2action</span><span class="p">)</span>
<div class="codehilite"><pre><span></span><span class="kn">import</span> <span class="nn">random</span>
<span class="n">f</span> <span class="o">=</span> <span class="n">Features</span><span class="p">()</span>
<span class="n">g1</span> <span class="o">=</span> <span class="n">Goalkeeper</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="n">g2</span> <span class="o">=</span> <span class="n">GoalkeeperWithGenerator</span><span class="p">(</span><span class="n">f</span><span class="p">)</span><span class="o">.</span><span class="n">behavior</span><span class="p">()</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">xrange</span><span class="p">(</span><span class="mi">10000</span><span class="p">):</span>
<span class="n">f</span><span class="o">.</span><span class="n">ballFar</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">random</span><span class="p">()</span> <span class="o">&gt;</span> <span class="mf">0.1</span>
<span class="n">f</span><span class="o">.</span><span class="n">ballOnLeft</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">random</span><span class="p">()</span> <span class="o">&lt;</span> <span class="mf">0.5</span>
<span class="n">g1action</span> <span class="o">=</span> <span class="n">g1</span><span class="o">.</span><span class="n">next</span><span class="p">()</span>
<span class="n">g2action</span> <span class="o">=</span> <span class="n">g2</span><span class="o">.</span><span class="n">next</span><span class="p">()</span>
<span class="k">print</span> <span class="s2">&quot;</span><span class="si">%s</span><span class="se">\t</span><span class="si">%s</span><span class="se">\t</span><span class="si">%s</span><span class="se">\t</span><span class="si">%s</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="p">(</span>
<span class="n">f</span><span class="o">.</span><span class="n">ballFar</span><span class="p">,</span> <span class="n">f</span><span class="o">.</span><span class="n">ballOnLeft</span><span class="p">,</span> <span class="n">g1action</span><span class="p">,</span> <span class="n">g2action</span><span class="p">)</span>
<span class="k">assert</span><span class="p">(</span><span class="n">g1action</span> <span class="o">==</span> <span class="n">g2action</span><span class="p">)</span>
</pre></div>

Loading…
Cancel
Save