automated update from build.py
This commit is contained in:
parent
5a9213d571
commit
06d911d482
@ -42,91 +42,91 @@
|
||||
</ol>
|
||||
<p>The usual approach to robot behavior design relies on hierarchical state machines. Specifically, we might be in a “standing” state while the ball is far away; when the ball becomes close, we enter a “diving” 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’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’ll have a class called Features that abstracts the robot’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’s behavior is specified by the <code>next()</code> function, which is called thirty times per second by the robot’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’s pretend that requirement 3 doesn’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">'stand'</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">'stand'</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">'diveLeft'</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">'diveLeft'</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="s1">'diveRight'</span>
|
||||
<span class="k">return</span> <span class="s1">'diveRight'</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’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’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">></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">></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">'stand'</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">'stand'</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">'diveLeft'</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">'diveLeft'</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">command</span> <span class="o">=</span> <span class="s1">'diveRight'</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">'diveRight'</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’s ugly. We’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’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’s needed. If you’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 “return” 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>
|
||||
<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">'stand'</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">'stand'</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">'diveLeft'</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">'diveLeft'</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">command</span> <span class="o">=</span> <span class="s1">'diveRight'</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">'diveRight'</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’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>
|
||||
<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="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">></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"><</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">"</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">"</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>
|
||||
<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">></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"><</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">"</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">"</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>
|
||||
|
||||
|
||||
|
120
feed.atom
120
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 “standing” state while the ball is far away; when the ball becomes close, we enter a “diving” 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’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’ll have a class called Features that abstracts the robot’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’s behavior is specified by the <code>next()</code> function, which is called thirty times per second by the robot’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’s pretend that requirement 3 doesn’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">'stand'</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">'stand'</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">'diveLeft'</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">'diveLeft'</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="s1">'diveRight'</span>
|
||||
<span class="k">return</span> <span class="s1">'diveRight'</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’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’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">></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">></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">'stand'</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">'stand'</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">'diveLeft'</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">'diveLeft'</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">command</span> <span class="o">=</span> <span class="s1">'diveRight'</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">'diveRight'</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’s ugly. We’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’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’s needed. If you’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 “return” 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>
|
||||
<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">'stand'</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">'stand'</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">'diveLeft'</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">'diveLeft'</span>
|
||||
<span class="k">else</span><span class="p">:</span>
|
||||
<span class="n">command</span> <span class="o">=</span> <span class="s1">'diveRight'</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">'diveRight'</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’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>
|
||||
<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="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">></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"><</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">"</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">"</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>
|
||||
<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">></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"><</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">"</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">"</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…
Reference in New Issue
Block a user