<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Tyrone</title>
  
  
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://fenggu.github.io/"/>
  <updated>2018-04-11T09:44:53.391Z</updated>
  <id>http://fenggu.github.io/</id>
  
  <author>
    <name>TyroneFgu</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>LeetCode数组题(中级)--螺旋矩阵</title>
    <link href="http://fenggu.github.io/2018/04/11/2018041101/"/>
    <id>http://fenggu.github.io/2018/04/11/2018041101/</id>
    <published>2018-04-11T09:52:34.000Z</published>
    <updated>2018-04-11T09:44:53.391Z</updated>
    
    <content type="html"><![CDATA[<p>给出一个 m x n 的矩阵（m 行, n 列），请按照顺时针螺旋顺序返回元素。</p><p>例如，给出以下矩阵：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> [ <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span> ],</span><br><span class="line"> [ <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span> ],</span><br><span class="line"> [ <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span> ]</span><br><span class="line">]</span><br></pre></td></tr></table></figure></p><p>应该返回 <code>[1,2,3,6,9,8,7,4,5]</code>。<br><a id="more"></a></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @param &#123;number[][]&#125; matrix</span></span><br><span class="line"><span class="comment"> * @return &#123;number[]&#125;</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">var</span> spiralOrder = <span class="function"><span class="keyword">function</span>(<span class="params">matrix</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> reArr = []</span><br><span class="line">    <span class="keyword">this</span>.reduceMat = <span class="function"><span class="keyword">function</span> (<span class="params">mat</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (!<span class="keyword">this</span>.isArray(mat)) <span class="keyword">return</span></span><br><span class="line">        <span class="keyword">let</span> shiftArr = mat.shift()</span><br><span class="line">        reArr = reArr.concat(shiftArr)</span><br><span class="line">        <span class="keyword">if</span> (!<span class="keyword">this</span>.isArray(mat)) <span class="keyword">return</span></span><br><span class="line">        mat.map(<span class="function">(<span class="params">arr</span>) =&gt;</span> &#123;</span><br><span class="line">            reArr.push(arr.pop())</span><br><span class="line">        &#125;)</span><br><span class="line">        <span class="keyword">if</span> (!<span class="keyword">this</span>.isArray(mat)) <span class="keyword">return</span></span><br><span class="line">        reArr = reArr.concat(mat.pop().reverse())</span><br><span class="line">        <span class="keyword">if</span> (!<span class="keyword">this</span>.isArray(mat)) <span class="keyword">return</span></span><br><span class="line">        mat.reverse().map(<span class="function">(<span class="params">arr</span>) =&gt;</span> &#123;</span><br><span class="line">            <span class="keyword">let</span> shiftArr = arr.shift()</span><br><span class="line">            reArr.push(shiftArr)</span><br><span class="line">        &#125;)</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">this</span>.isArray(mat)) &#123;</span><br><span class="line">            reduceMat(mat.reverse())</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">this</span>.isArray = <span class="function"><span class="keyword">function</span> (<span class="params">mat</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">Object</span>.prototype.toString.call(mat[<span class="number">0</span>]) === <span class="string">'[object Array]'</span> &amp;&amp; mat[<span class="number">0</span>].length &gt; <span class="number">0</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">this</span>.reduceMat(matrix)</span><br><span class="line">    <span class="keyword">return</span> reArr</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;给出一个 m x n 的矩阵（m 行, n 列），请按照顺时针螺旋顺序返回元素。&lt;/p&gt;
&lt;p&gt;例如，给出以下矩阵：&lt;br&gt;&lt;figure class=&quot;highlight js&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;[&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; [ &lt;span class=&quot;number&quot;&gt;1&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;2&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;3&lt;/span&gt; ],&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; [ &lt;span class=&quot;number&quot;&gt;4&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;5&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;6&lt;/span&gt; ],&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt; [ &lt;span class=&quot;number&quot;&gt;7&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;8&lt;/span&gt;, &lt;span class=&quot;number&quot;&gt;9&lt;/span&gt; ]&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;应该返回 &lt;code&gt;[1,2,3,6,9,8,7,4,5]&lt;/code&gt;。&lt;br&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>使用react结合Immutable实现一道五子棋面试题(下)</title>
    <link href="http://fenggu.github.io/2018/03/26/2018032602/"/>
    <id>http://fenggu.github.io/2018/03/26/2018032602/</id>
    <published>2018-03-26T06:25:34.000Z</published>
    <updated>2018-03-28T08:31:22.000Z</updated>
    
    <content type="html"><![CDATA[<p>一道面试题,要求如下：</p><ul><li>用web技术实现一个五子棋</li><li>支持DOM和Canvas版本切换</li><li>实现悔棋功能</li><li>实现一个撤销悔棋功能</li></ul><p>前文: <a href="https://fenggu.github.io/2018/03/26/2018032601/">使用react结合Immutable实现一道五子棋面试题(上)</a><br>github: <a href="https://github.com/fenggu/gomoku" target="_blank" rel="noopener">https://github.com/fenggu/gomoku</a><br><a id="more"></a></p><h4 id="Canvas版开发"><a href="#Canvas版开发" class="headerlink" title="Canvas版开发"></a>Canvas版开发</h4><p>首先创建一个<code>&lt;canvas /&gt;</code>标签，然后使用<code>canvas</code>api来进行棋盘和棋子的绘制。<code>canvas</code>版本的棋盘数据也是来自<code>store</code>与<code>dom</code>版共用一份数据，这样可以达到<code>dom</code>和<code>canvas</code>版本无缝切换的效果<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line">renderBox = <span class="function">(<span class="params">gomoku</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> canvas = <span class="keyword">this</span>.refs.canvas</span><br><span class="line">  <span class="keyword">if</span> (canvas.getContext) &#123;</span><br><span class="line">    <span class="keyword">let</span> ctx = canvas.getContext(<span class="string">'2d'</span>)</span><br><span class="line">    ctx.clearRect(<span class="number">0</span>, <span class="number">0</span>, <span class="number">450</span>, <span class="number">450</span>)</span><br><span class="line">    ctx.fillStyle = <span class="string">'#333'</span>;</span><br><span class="line">    ctx.beginPath()</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; <span class="number">16</span>; i++) &#123;</span><br><span class="line">      ctx.moveTo(<span class="number">15</span>, <span class="number">30</span> * i + <span class="number">15</span>)</span><br><span class="line">      ctx.lineTo(<span class="number">435</span>, <span class="number">30</span> * i + <span class="number">15</span>)</span><br><span class="line">      ctx.stroke()</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; <span class="number">16</span>; i++) &#123;</span><br><span class="line">      ctx.moveTo(<span class="number">30</span> * i + <span class="number">15</span>, <span class="number">15</span>)</span><br><span class="line">      ctx.lineTo(<span class="number">30</span> * i + <span class="number">15</span>, <span class="number">435</span>)</span><br><span class="line">      ctx.stroke()</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> x = <span class="number">0</span>; x &lt; gomoku.size; x ++) &#123;</span><br><span class="line">      <span class="keyword">for</span> (<span class="keyword">let</span> y = <span class="number">0</span>; y &lt; gomoku.get(x).size; y++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (gomoku.get(x).get(y) !== <span class="number">0</span>) &#123;</span><br><span class="line">          ctx.beginPath()</span><br><span class="line">          ctx.fillStyle = gomoku.get(x).get(y) === <span class="number">1</span> ? <span class="string">'#fff'</span> : <span class="string">'#000'</span></span><br><span class="line">          ctx.arc(<span class="number">15</span> + y * <span class="number">30</span>, <span class="number">15</span> + x * <span class="number">30</span>, <span class="number">15</span>, <span class="number">0</span>, <span class="built_in">Math</span>.PI*<span class="number">2</span>, <span class="literal">true</span>)</span><br><span class="line">          ctx.fill()</span><br><span class="line">          ctx.fillStyle = gomoku.get(x).get(y) === <span class="number">1</span> ? <span class="string">'#000'</span> : <span class="string">'#fff'</span></span><br><span class="line">          ctx.arc(<span class="number">15</span> + y * <span class="number">30</span>, <span class="number">15</span> + x * <span class="number">30</span>, <span class="number">15</span>, <span class="number">0</span>, <span class="built_in">Math</span>.PI*<span class="number">2</span>, <span class="literal">true</span>)</span><br><span class="line">          ctx.stroke()</span><br><span class="line">          ctx.closePath();</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>然后还是使用上面的<code>gomoku()</code>方法，只是改一下<code>x</code> <code>y</code>的获取方式改成点击事件的坐标。然后通过减去棋盘的<code>offsetLeft</code>和<code>offsetTop</code>然后除以棋子的宽度取整。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> y = <span class="built_in">Math</span>.floor((e.pageX - e.target.offsetLeft) / <span class="number">30</span>)</span><br><span class="line"><span class="keyword">let</span> x = <span class="built_in">Math</span>.floor((e.pageY - e.target.offsetTop) / <span class="number">30</span>)</span><br></pre></td></tr></table></figure></p><p>效果图：<br><img src="/images/gomoku-canvas.png" alt="img"></p><h4 id="输赢判断"><a href="#输赢判断" class="headerlink" title="输赢判断"></a>输赢判断</h4><p>每一次下棋都会有一次输赢判断，会以点击坐标为原点然后遍历上下左右，右上到左下，左上到右下6个方向。如果有连续相同颜色的5个棋子则胜利。因为<code>DOM</code>版和<code>Canvas</code>版是共用同一套逻辑的输赢判断。因此这里我采用了高阶组件的方式来传入输赢判断的函数。<br>高阶组件<code>goMokuMixin</code>代码：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React, &#123; Component &#125; <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; connect &#125; <span class="keyword">from</span> <span class="string">'react-redux'</span></span><br><span class="line"><span class="keyword">import</span> &#123; bindActionCreators &#125; <span class="keyword">from</span> <span class="string">'redux'</span></span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> goMokuAction <span class="keyword">from</span> <span class="string">'../../actions/gomoku'</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> (ComposedComponent) =&gt; &#123;</span><br><span class="line">  <span class="comment">/* Redux bind */</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">mapStateToProps</span> (<span class="params">state</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> index = state.get(<span class="string">'index'</span>)</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">      index: index,</span><br><span class="line">      gomoku: state.get(<span class="string">'gomoku'</span>).get(index),</span><br><span class="line">      win: state.get(<span class="string">'win'</span>),</span><br><span class="line">      size: state.get(<span class="string">'gomoku'</span>).size</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">mapDispatchToProps</span> (<span class="params">dispatch</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">      actions: bindActionCreators(<span class="built_in">Object</span>.assign(&#123;&#125;, goMokuAction), dispatch)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  @connect(mapStateToProps, mapDispatchToProps)</span><br><span class="line">  <span class="class"><span class="keyword">class</span> <span class="title">goMokuMixin</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">    isWin = <span class="function">(<span class="params">x, y, isCurrent</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">this</span>.props.actions.changeWin(&#123;</span><br><span class="line">        role: <span class="number">0</span>,</span><br><span class="line">        size: <span class="literal">null</span></span><br><span class="line">      &#125;)</span><br><span class="line">      <span class="keyword">let</span> pieces = <span class="keyword">this</span>.props.gomoku</span><br><span class="line">      <span class="keyword">let</span> sum = &#123;</span><br><span class="line">        lrCount: &#123;</span><br><span class="line">          count: <span class="number">1</span>,</span><br><span class="line">          order: &#123;</span><br><span class="line">            <span class="number">0</span>: [<span class="number">-1</span>, <span class="number">0</span>],</span><br><span class="line">            <span class="number">1</span>: [<span class="number">1</span>, <span class="number">0</span>]</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        udCount: &#123;</span><br><span class="line">          count: <span class="number">1</span>,</span><br><span class="line">          order: &#123;</span><br><span class="line">            <span class="number">0</span>: [<span class="number">0</span>, <span class="number">-1</span>],</span><br><span class="line">            <span class="number">1</span>: [<span class="number">0</span>, <span class="number">1</span>]</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        luCount: &#123;</span><br><span class="line">          count: <span class="number">1</span>,</span><br><span class="line">          order: &#123;</span><br><span class="line">            <span class="number">0</span>: [<span class="number">-1</span>, <span class="number">1</span>],</span><br><span class="line">            <span class="number">1</span>: [<span class="number">1</span>, <span class="number">-1</span>]</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        rdCount: &#123;</span><br><span class="line">          count: <span class="number">1</span>,</span><br><span class="line">          order: &#123;</span><br><span class="line">            <span class="number">0</span>: [<span class="number">1</span>, <span class="number">1</span>],</span><br><span class="line">            <span class="number">1</span>: [<span class="number">-1</span>, <span class="number">-1</span>]</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">for</span> (<span class="keyword">let</span> co <span class="keyword">in</span> sum) &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> o <span class="keyword">in</span> sum[co].order) &#123;</span><br><span class="line">          <span class="keyword">let</span> constantX = sum[co].order[o][<span class="number">0</span>]</span><br><span class="line">          <span class="keyword">let</span> constantY = sum[co].order[o][<span class="number">1</span>]</span><br><span class="line">          <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">1</span>; i &lt; <span class="number">5</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span> ((x + constantX * i) &gt;= <span class="number">15</span> || (x + constantX * i) &lt; <span class="number">0</span> || (y + constantY * i) &gt;= <span class="number">15</span> || (y + constantY * i) &lt; <span class="number">0</span>) &#123;</span><br><span class="line">              <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (pieces.get(x + constantX * i).get(y + constantY * i) !== isCurrent) &#123;</span><br><span class="line">              <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            sum[co].count++</span><br><span class="line">            <span class="keyword">this</span>.isOverCount(sum[co].count)</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    isOverCount = <span class="function">(<span class="params">count</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">let</span> isCurrent = <span class="keyword">this</span>.props.index % <span class="number">2</span> === <span class="number">0</span> ? <span class="number">2</span> : <span class="number">1</span></span><br><span class="line">      <span class="keyword">if</span> (count &gt;= <span class="number">5</span>) &#123;</span><br><span class="line">        <span class="keyword">this</span>.props.actions.changeWin(&#123;</span><br><span class="line">          role: isCurrent,</span><br><span class="line">          size: <span class="keyword">this</span>.props.size</span><br><span class="line">        &#125;)</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    render() &#123;</span><br><span class="line">      <span class="keyword">return</span> (&lt;ComposedComponent isWin=&#123;this.isWin&#125; /&gt;);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  return goMokuMixin</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="时光旅行"><a href="#时光旅行" class="headerlink" title="时光旅行"></a>时光旅行</h4><p>因为题目要求上有悔棋以及撤销悔棋功能，这里我们采用<code>store</code>里的<code>index</code>的更改，来实现整场对局的时光旅行。因为随着对局次数越来越多，棋盘的数据也会越来越大，我采用了Immutable来存储对局的棋盘。<br><code>gomoku</code>reducer:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> Immutable <span class="keyword">from</span> <span class="string">'immutable'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; PUSH_GOMOKU, GOMOKU_RESTORE &#125; <span class="keyword">from</span> <span class="string">'../constants/actionTypes'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> createData = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> boxs = []</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> x = <span class="number">0</span>; x &lt; <span class="number">15</span>; x++) &#123;</span><br><span class="line">    boxs.push([])</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> y = <span class="number">0</span>; y &lt; <span class="number">15</span>; y++) &#123;</span><br><span class="line">      boxs[x].push(<span class="number">0</span>)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> [boxs]</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> initialState = Immutable.fromJS(createData())</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> (state = initialState, action) =&gt; &#123;</span><br><span class="line">  <span class="keyword">switch</span> (action.type) &#123;</span><br><span class="line">    <span class="keyword">case</span> PUSH_GOMOKU:</span><br><span class="line">      <span class="keyword">return</span> state.splice(action.index).push(action.moku)</span><br><span class="line">    <span class="keyword">case</span> GOMOKU_RESTORE:</span><br><span class="line">      <span class="keyword">return</span> initialState</span><br><span class="line">    <span class="keyword">default</span>:</span><br><span class="line">      <span class="keyword">return</span> state;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>所以我们是以<code>this.props.gomoku.get(index)</code>的形式来提取当前对局数据。也就是说只要将<code>index</code>的值-1即是悔棋，+1则是撤销悔棋。同时利用这个，我们也可以顺便实现一个对局回放的功能。即是将index的值置0然后定时增加即刻。<br><img src="/images/gomoku.gif" alt="img"></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;一道面试题,要求如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用web技术实现一个五子棋&lt;/li&gt;
&lt;li&gt;支持DOM和Canvas版本切换&lt;/li&gt;
&lt;li&gt;实现悔棋功能&lt;/li&gt;
&lt;li&gt;实现一个撤销悔棋功能&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;前文: &lt;a href=&quot;https://fenggu.github.io/2018/03/26/2018032601/&quot;&gt;使用react结合Immutable实现一道五子棋面试题(上)&lt;/a&gt;&lt;br&gt;github: &lt;a href=&quot;https://github.com/fenggu/gomoku&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/fenggu/gomoku&lt;/a&gt;&lt;br&gt;
    
    </summary>
    
    
      <category term="React" scheme="http://fenggu.github.io/tags/React/"/>
    
      <category term="Redux" scheme="http://fenggu.github.io/tags/Redux/"/>
    
      <category term="Immutable" scheme="http://fenggu.github.io/tags/Immutable/"/>
    
  </entry>
  
  <entry>
    <title>使用react结合Immutable实现一道五子棋面试题(上)</title>
    <link href="http://fenggu.github.io/2018/03/26/2018032601/"/>
    <id>http://fenggu.github.io/2018/03/26/2018032601/</id>
    <published>2018-03-26T03:25:34.000Z</published>
    <updated>2018-03-28T08:29:53.000Z</updated>
    
    <content type="html"><![CDATA[<h4 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h4><p>一道朋友的面试题,要求如下：</p><ul><li>用web技术实现一个五子棋</li><li>支持DOM和Canvas版本切换</li><li>实现悔棋功能</li><li>实现一个撤销悔棋功能</li></ul><p>看起来挺有意思的,便用<code>react</code> <code>redux</code> <code>Immutable</code>等技术自己实现了一个。<br>github: <a href="https://github.com/fenggu/gomoku" target="_blank" rel="noopener">https://github.com/fenggu/gomoku</a><br><a id="more"></a></p><h4 id="Store数据结构"><a href="#Store数据结构" class="headerlink" title="Store数据结构"></a>Store数据结构</h4><p>因为要支持DOM和Canvas版本切换的切换，所以采用<code>react</code>这种单向数据流的框架再适合不过了，只需要让DOM和Canvas版本公用一套数据流，然后做好View层的显示就好了。所以首先我们先来定义我们的<code>store</code>。<br><code>store</code>对象结构如下：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">gomoku: [</span><br><span class="line">  [</span><br><span class="line">    [<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>],</span><br><span class="line">    [<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>],</span><br><span class="line">    [<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>],</span><br><span class="line">    [<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>],</span><br><span class="line">    [<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>],</span><br><span class="line">    [<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>],</span><br><span class="line">    [<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>],</span><br><span class="line">    [<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>],</span><br><span class="line">    [<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>],</span><br><span class="line">    [<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>],</span><br><span class="line">    [<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>],</span><br><span class="line">    [<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>],</span><br><span class="line">    [<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>],</span><br><span class="line">    [<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>],</span><br><span class="line">    [<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>]</span><br><span class="line">  ]</span><br><span class="line">],</span><br><span class="line">index: <span class="number">0</span>, <span class="comment">// 对局索引</span></span><br><span class="line">win: &#123; <span class="comment">// 胜利状态</span></span><br><span class="line">  role: <span class="number">0</span>, <span class="comment">// 胜利对象</span></span><br><span class="line">  size: <span class="literal">null</span> <span class="comment">// 胜利时的棋盘数组长度</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>其中棋盘上的棋子数据以二维数组<code>pieces[x][y]：value</code>的形式存储,<br>0代表空，1代表白，2代表黑。<br><code>store</code>里的<code>gomoku</code>将每一步的“棋盘数据”都存储进数组里，并通过<code>Immutable</code>的API:<code>gomoku.get(index)</code>的形式读取。所以我们将每一步的序列索引<code>index</code>也存进<code>store</code>中，这样也极大的方便开发时间旅行功能。<br>因为随着下棋步数越来越多，<code>gomoku</code>里存储的数组也会越来越大。为了解决这个问题，一般的做法是使用 shallowCopy（浅拷贝）或 deepCopy（深拷贝）来避免被修改，但这样做造成了 CPU 和内存的浪费。<br>Immutable 可以很好地解决这些问题。Immutable Data 就是一旦创建，就不能再被更改的数据。对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象。Immutable 实现的原理是 Persistent Data Structure（持久化数据结构），也就是使用旧数据创建新数据时，要保证旧数据同时可用且不变。同时为了避免 deepCopy 把所有节点都复制一遍带来的性能损耗。</p><h4 id="DOM版开发"><a href="#DOM版开发" class="headerlink" title="DOM版开发"></a>DOM版开发</h4><p>快速搭建完基本框架后，就可以进行愉快的DOM版本开发了。（基本框架代码参考顶部项目GitHub）</p><h5 id="绘制棋盘"><a href="#绘制棋盘" class="headerlink" title="绘制棋盘"></a>绘制棋盘</h5><p>通过了解五子棋棋盘是15x15的格子布局,所以我采用<code>div</code>堆叠的方式来绘制这个棋盘。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">renderBox = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> boxs = []</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; <span class="number">14</span>; i ++) &#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> j = <span class="number">0</span>; j &lt; <span class="number">14</span>; j++) &#123;</span><br><span class="line">      boxs.push(&lt;div key=&#123;`b-$&#123;i&#125;-$&#123;j&#125;`&#125;  className='gomoku-box'&gt;&lt;/div&gt;)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> boxs</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h5 id="绘制棋子"><a href="#绘制棋子" class="headerlink" title="绘制棋子"></a>绘制棋子</h5><p>然后是棋子的布置，我们知道五子棋是下在棋盘线路的交叉点上，因此这里我采用16x16个透明格子的形式将棋子的格子铺在棋盘上，通过错位而达到棋子定位的效果。这样同时也方便了点击事件绑定。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">renderPieces = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> boxs = []</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; <span class="number">15</span>; i ++) &#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> j = <span class="number">0</span>; j &lt; <span class="number">15</span>; j++) &#123;</span><br><span class="line">      boxs.push(&lt;Piece key=&#123;`$&#123;i&#125;-$&#123;j&#125;`&#125; x=&#123;i&#125; y=&#123;j&#125; isWin=&#123;this.props.isWin&#125; class='gomoku-box'&gt;&lt;/Piece&gt;)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> (boxs)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>render<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">render() &#123;</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;div className=<span class="string">"gomoku"</span>&gt;</span><br><span class="line">      &lt;div className=<span class="string">"gomoku-pieces"</span>&gt;</span><br><span class="line">        &#123; <span class="keyword">this</span>.renderPieces() &#125;</span><br><span class="line">      &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">      &lt;div className="gomoku-back"&gt;</span></span><br><span class="line"><span class="regexp">        &#123; this.renderBox() &#125;</span></span><br><span class="line"><span class="regexp">      &lt;/</span>div&gt;</span><br><span class="line">    &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">  );</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure></p><h5 id="棋子组件"><a href="#棋子组件" class="headerlink" title="棋子组件"></a>棋子组件</h5><p>每一个棋子都是一个<code>Piece</code>小组件，<code>Piece</code>接受<code>x</code> <code>y</code>两个参数用来确定棋子的位置，在触发点击事件的同时，会将<code>x</code> <code>y</code>参数组成一个新的棋盘，并且通过<code>dispatch action</code>来更新棋盘的数据。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">goMoku = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// 黑: 2, 白: 1, 空: 0</span></span><br><span class="line">  <span class="keyword">let</span> x = <span class="keyword">this</span>.props.x</span><br><span class="line">  <span class="keyword">let</span> y = <span class="keyword">this</span>.props.y</span><br><span class="line">  <span class="keyword">if</span> ( <span class="keyword">this</span>.props.gomoku.get(x).get(y) !== <span class="number">0</span> || <span class="keyword">this</span>.props.win.role !== <span class="number">0</span>) <span class="keyword">return</span></span><br><span class="line">  <span class="keyword">let</span> isCurrent = <span class="keyword">this</span>.props.index % <span class="number">2</span> === <span class="number">0</span> ? <span class="number">2</span> : <span class="number">1</span></span><br><span class="line">  <span class="keyword">let</span> newGoMoku = <span class="keyword">this</span>.props.gomoku.setIn([x, y], isCurrent)</span><br><span class="line">  <span class="keyword">this</span>.props.actions.pushGoMoku(newGoMoku, <span class="keyword">this</span>.props.index + <span class="number">1</span>)</span><br><span class="line">  <span class="keyword">this</span>.props.actions.changeGoMokuIndex(<span class="keyword">this</span>.props.index + <span class="number">1</span>)</span><br><span class="line">  <span class="keyword">this</span>.props.isWin(x, y, isCurrent)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>同时为了防止棋子的重复渲染我们在<code>Piece</code>的<code>componentWillReceiveProps</code>中加入条件判断。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">componentWillReceiveProps (nextProps) &#123;</span><br><span class="line">  <span class="keyword">let</span> x = <span class="keyword">this</span>.props.x</span><br><span class="line">  <span class="keyword">let</span> y = <span class="keyword">this</span>.props.y</span><br><span class="line">  <span class="keyword">if</span> (nextProps.gomoku.get(x).get(y) == <span class="keyword">this</span>.props.gomoku.get(x).get(y)) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>效果图：<br><img src="/images/gomoku-dom.png" alt="img"><br>下文: <a href="https://fenggu.github.io/2018/03/26/2018032602/">使用react结合Immutable实现一道五子棋面试题(下)</a></p>]]></content>
    
    <summary type="html">
    
      &lt;h4 id=&quot;引言&quot;&gt;&lt;a href=&quot;#引言&quot; class=&quot;headerlink&quot; title=&quot;引言&quot;&gt;&lt;/a&gt;引言&lt;/h4&gt;&lt;p&gt;一道朋友的面试题,要求如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用web技术实现一个五子棋&lt;/li&gt;
&lt;li&gt;支持DOM和Canvas版本切换&lt;/li&gt;
&lt;li&gt;实现悔棋功能&lt;/li&gt;
&lt;li&gt;实现一个撤销悔棋功能&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;看起来挺有意思的,便用&lt;code&gt;react&lt;/code&gt; &lt;code&gt;redux&lt;/code&gt; &lt;code&gt;Immutable&lt;/code&gt;等技术自己实现了一个。&lt;br&gt;github: &lt;a href=&quot;https://github.com/fenggu/gomoku&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/fenggu/gomoku&lt;/a&gt;&lt;br&gt;
    
    </summary>
    
    
      <category term="React" scheme="http://fenggu.github.io/tags/React/"/>
    
      <category term="Redux" scheme="http://fenggu.github.io/tags/Redux/"/>
    
      <category term="Immutable" scheme="http://fenggu.github.io/tags/Immutable/"/>
    
  </entry>
  
</feed>
