<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>CS-Tao · Blog</title>
  
  <subtitle>这是一个不能停留太久的世界</subtitle>
  <link href="/blog/atom.xml" rel="self"/>
  
  <link href="https://cs-tao.github.io/blog/"/>
  <updated>2025-01-28T04:15:07.206Z</updated>
  <id>https://cs-tao.github.io/blog/</id>
  
  <author>
    <name>CS-Tao</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>漂泊的 2024</title>
    <link href="https://cs-tao.github.io/blog/%E7%94%9F%E6%B4%BB/%E6%BC%82%E6%B3%8A%E7%9A%842024/"/>
    <id>https://cs-tao.github.io/blog/生活/漂泊的2024/</id>
    <published>2025-01-28T04:13:20.000Z</published>
    <updated>2025-01-28T04:15:07.206Z</updated>
    
    <content type="html"><![CDATA[<p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/my-2024/xg04.jpg" width="40%" height="40%"></p><p>或许是疫情的余温，近两年的经济形势非常差，对我的工作也产生了深远的影响，去年 OpenAI 发布了 ChatGPT 🤖，我们公司不久也启动了若干 AI 相关的项目，我有幸（or not?…doesn’t matter）参与了其中一个，从那之后直到现在，漂泊的生活便没有停止过，我记忆中的 27 岁似乎比 26 岁更加漫长。</p><a id="more"></a><hr><p>23 年上半年在一个不高不低的点买了房 🏠，原本计划着下半年买车 🚗，接着装修 🪜… 现在想想不知道还要等多久才能继续当时的规划。</p><p>去年 9 月份收到了通知，说我们有机会参加公司的某个 AI 相关的㊙️密项目，征求大家的参与意愿，想着能搭上 AI 这趟高速列车 🚄，我便决定参与到这个项目中，代价是未来几个月需要和来自各个工区的同事在杭州工区封闭开发项目。封闭的工作强度蛮高，但这种有目标的忙碌让人感到充实和安心。</p><p>至少在 2023 向我们挥手告别时，我们对未来是充满信心的，都期盼着更好的 2024。</p><h1 id="🗓️-壹月"><a href="#🗓️-壹月" class="headerlink" title="🗓️ 壹月"></a>🗓️ 壹月</h1><p>去年项目封闭结束后我们便回到了武汉工区，因为项目还处于前期开发阶段，我们部门的员工又分部在全国各地，于是项目组提出了「旅游办公」的方案，也就是在上海、杭州、深圳各待几周集中开发。</p><p>一月份我们主要在深圳办公，不得感叹深圳的办公环境比其它工区好很多，深圳工区的办公大楼的视野非常好，和香港隔海相望，于是趁着这个机会和同事去香港体验了一下 City-Walk，顺便买了琦姐心心念念的相机。</p><table><thead><tr><th style="text-align:center"></th><th style="text-align:center"></th></tr></thead><tbody><tr><td style="text-align:center"><img src="http://home.cs-tao.cc/github-content/contents/blog/image/my-2024/xg1.jpg" alt="xg1.jpg"></td><td style="text-align:center"><img src="http://home.cs-tao.cc/github-content/contents/blog/image/my-2024/xg02.jpg" alt="xg02.jpg"></td></tr><tr><td style="text-align:center"><img src="http://home.cs-tao.cc/github-content/contents/blog/image/my-2024/xg03.jpg" alt="xg03.jpg"></td><td style="text-align:center"><img src="http://home.cs-tao.cc/github-content/contents/blog/image/my-2024/xg04.jpg" alt="xg04.jpg"></td></tr></tbody></table><h1 id="🗓️-贰月"><a href="#🗓️-贰月" class="headerlink" title="🗓️ 贰月"></a>🗓️ 贰月</h1><p>二月最重要的事情就是过年啦 🧨，春节假期恰逢百年难得一遇的冻雨，难以忘记<a href="https://home.cs-tao.cc/blog/生活/第一次开车回家/" target="_blank" rel="noopener">第一次开车回家 🚗</a> 的体验。</p><p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/20240206/17.jpg" width="40%" height="40%"></p><h1 id="🗓️-叁月"><a href="#🗓️-叁月" class="headerlink" title="🗓️ 叁月"></a>🗓️ 叁月</h1><p>去年租的房子到期了，选了很久选到了一个有停车位且环境非常不错的小区，离公司也很近，不得感叹生活越来越好啦！</p><p>三月底和琦姐的朋友们去武大看了樱花 🌸，当时正值晚樱，是最适合赏樱的时间，微风把樱花如细雨般吹落到地上，还是和第一次看见落樱时一样惊艳——希望明年也有机会回珈看樱花。</p><p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/my-2024/yh2.gif" width="40%" height="40%"></p><h1 id="🗓️-肆月"><a href="#🗓️-肆月" class="headerlink" title="🗓️ 肆月"></a>🗓️ 肆月</h1><p>四月份公司高层对公司的布局做了很大的调整，撤销了我们部门的武汉分部，给大家发放 N+1 补偿，或者把工资提高 25% 然后转岗到上海（因为武汉的工资是一线城市的 0.8 倍）。</p><p>N+1 和转岗这两个选择让人感到迷茫，似乎又是一次人生道路上的转折点。</p><blockquote><p>1️⃣ 如果选择转岗到上海，便需要在上海重新租房，并经常来回上海-武汉，提高的工资确实能 cover 这部分开销，不过可以想象到未来工作的疲惫程度。<br>2️⃣ 如果选择领取 N+1 补偿，便面临着和武汉同时失业同事们竞争本就不多的岗位，最后的工资福利和现有一定会存在较大落差，不利于长期发展。</p></blockquote><p>和琦姐商量了两天，期间在两种方案中摇摆不定，最后决定转岗到上海，五月底开始在上海办公。</p><p>直到现在我也不知道这是否是一个正确的决定。</p><h1 id="🗓️-伍月"><a href="#🗓️-伍月" class="headerlink" title="🗓️ 伍月"></a>🗓️ 伍月</h1><p>五一假期和琦姐自驾 🚗 到成都看汤尤杯比赛，非常幸运和运动员们住在同一个酒店，和喜欢的运动员一起吃早饭是一件很奇妙的事情。随着赛事圆满结束，我们也圆满地离开了成都。</p><table><thead><tr><th style="text-align:center"></th><th style="text-align:center"></th></tr></thead><tbody><tr><td style="text-align:center"><img src="http://home.cs-tao.cc/github-content/contents/blog/image/my-2024/zj1.jpeg" alt="zj1.jpeg"></td><td style="text-align:center"><img src="http://home.cs-tao.cc/github-content/contents/blog/image/my-2024/zj2.jpeg" alt="zj2.jpeg"></td></tr><tr><td style="text-align:center"><img src="http://home.cs-tao.cc/github-content/contents/blog/image/my-2024/tyb1.jpeg" alt="tyb1.jpeg"></td><td style="text-align:center"><img src="http://home.cs-tao.cc/github-content/contents/blog/image/my-2024/tyb2.jpeg" alt="tyb2.jpeg"></td></tr></tbody></table><p>上海和武汉有三个半小时的车程，每周往返的成本倒是不算高，也就习惯了这样的工作节奏，和同事在上海租了房子，正式开启沪漂之旅。</p><p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/my-2024/chj.jpeg" width="40%" height="40%"></p><h1 id="🗓️-陆月"><a href="#🗓️-陆月" class="headerlink" title="🗓️ 陆月"></a>🗓️ 陆月</h1><p>端午节和朋友们到洛阳玩，和当地羽协打了交流赛，到龙门石窟、古墓博物馆、白马寺、洛邑古镇打卡，不长的假期却过得非常充实。</p><p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/my-2024/lmsk.jpeg" width="40%" height="40%"></p><p>六月份琦姐毕业了，工作地点在青山区，于是我们舍弃了这个刚租到的我们非常满意的小区，重新在青山租房。</p><p>也是这个月，我们忙碌了大半年的 AI 项目计划着正式对外上线，我需要负责其中一个上线前的改版需求，这期间一直在北京出差。在北京和同样从武汉来北京漂泊的朋友们吃了个饭，在知春路上聊着各自加入的新团队。</p><p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/my-2024/zcl.jpeg" width="40%" height="40%"></p><h1 id="🗓️-柒月"><a href="#🗓️-柒月" class="headerlink" title="🗓️ 柒月"></a>🗓️ 柒月</h1><p>婆婆去世，请了几天丧假，再一次开车从武汉回家，开了十个小时。第二天一大早开车回老家，自高中毕业后就再也没有见过老家夏天的样子，漫山绿油油的玉米树让我很怀念小时候暑假回老家避暑的日子。</p><p>处理完家里的事情便回到工作中，后续一两周都在上海办公。</p><p>不巧的是，因为公司 AI 战略的调整，我们团队的工作重心改为支援一个海外项目，项目主要成员在深圳，所以后面我们需要长期在深圳出差了，真是世事难料。</p><p>海外项目差不多两周后，公司高层觉得当前业务的国内版本优先级比国外项目更高，于是我们又投入了一个月去支援国内项目，不过和负责人沟通后我们可以在武汉工区办公，这两周过得非常快乐。</p><h1 id="🗓️-捌月"><a href="#🗓️-捌月" class="headerlink" title="🗓️ 捌月"></a>🗓️ 捌月</h1><p>八月份不出意外，又回到了海外项目继续之前未完成的工作。不得不说我非常喜欢在深圳出差时住的酒店，楼下有我喜欢的鲜榨甘蔗汁和福鼎肉片，每天下班回去买一份幸福感很高（不过每次都得祈祷一下城管不在）。</p><p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/my-2024/sz1.jpeg" width="40%" height="40%"></p><h1 id="🗓️-玖月"><a href="#🗓️-玖月" class="headerlink" title="🗓️ 玖月"></a>🗓️ 玖月</h1><p>令人焦虑又兴奋的一个月，因为需要筹划去琦姐家见家长的事情，准备了很久礼物。</p><h1 id="🗓️-拾月"><a href="#🗓️-拾月" class="headerlink" title="🗓️ 拾月"></a>🗓️ 拾月</h1><p>国庆节开车去琦姐家里，一路南下并没有堵车，和这趟路途一样，一切都很顺利。</p><p>令人难忘的国庆，每天都很充实，希望有时间能单独记录一下这段旅程。</p><table><thead><tr><th style="text-align:center"></th><th style="text-align:center"></th></tr></thead><tbody><tr><td style="text-align:center"><img src="http://home.cs-tao.cc/github-content/contents/blog/image/my-2024/js01.jpeg" alt="js01.jpeg"></td><td style="text-align:center"><img src="http://home.cs-tao.cc/github-content/contents/blog/image/my-2024/js02.jpeg" alt="js02.jpeg"></td></tr><tr><td style="text-align:center"><img src="http://home.cs-tao.cc/github-content/contents/blog/image/my-2024/js03.jpeg" alt="js03.jpeg"></td><td style="text-align:center"><img src="http://home.cs-tao.cc/github-content/contents/blog/image/my-2024/js04.jpeg" alt="js04.jpeg"></td></tr></tbody></table><p>离开吉安的时候阿姨送了一个车载玉坠，没有比这个更漂亮的挂饰了（在这之前我看过很多车载挂饰，都没有找到满意的，所以一直没有买）。</p><p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/my-2024/gz.jpeg" width="40%" height="40%"></p><p>这个月剩下的时间仍然在深圳出差，深圳的天气真的很适合打工人。</p><p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/my-2024/sz.jpeg" width="40%" height="40%"></p><h1 id="🗓️-拾壹月"><a href="#🗓️-拾壹月" class="headerlink" title="🗓️ 拾壹月"></a>🗓️ 拾壹月</h1><p>公司出了新的出差政策，从武汉到深圳这种行程不能直接走公司平台预订，需要经过 Leader 审批后报销。所以为了省去一些麻烦，我们这个月便回到了上海办公。</p><p>期间还去了趟苏州，在阳澄湖吃了大闸蟹，也第一次尝试了钓鱼 🎣，不过啥也没钓到。</p><p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/my-2024/dy.jpeg" width="40%" height="40%"></p><p>这个月海外项目第一版基本完成了，上层暂缓了第二版的计划，我也在这个间隙和 Leader 提了转岗申请，协商在下个月完成项目交接和转岗流程。</p><h1 id="🗓️-拾贰月"><a href="#🗓️-拾贰月" class="headerlink" title="🗓️ 拾贰月"></a>🗓️ 拾贰月</h1><p>这个月主要的工作是给海外项目收尾，接着去深圳完成交接，然后加入上海的新团队。</p><p>今年的年终总决赛在杭州开打，我和琦姐分别从上海武汉出发，来杭州看了雅思最后一场国际比赛。这一次的酒店比起上次在成都的差了很多，但长期出差积攒了行政酒廊权益，不用为晚上吃啥顾虑了。</p><table><thead><tr><th style="text-align:center"></th><th style="text-align:center"></th></tr></thead><tbody><tr><td style="text-align:center"><img src="http://home.cs-tao.cc/github-content/contents/blog/image/my-2024/ys.jpeg" alt="ys.jpeg"></td><td style="text-align:center"><img src="http://home.cs-tao.cc/github-content/contents/blog/image/my-2024/zjs2.jpeg" alt="zjs2.jpeg"></td></tr></tbody></table><p>比赛看罢，送琦姐去杭州站，我也在最后十分钟赶上了杭州东站回上海的动车 🚄。</p><p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/my-2024/cp.jpeg" width="40%" height="40%"></p><h1 id="25-年元旦"><a href="#25-年元旦" class="headerlink" title="25 年元旦"></a>25 年元旦</h1><p>去归元寺求了签，签运不错，希望新的一年越来越顺利，拜拜 2024 👋。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;http://home.cs-tao.cc/github-content/contents/blog/image/my-2024/xg04.jpg&quot; width=&quot;40%&quot; height=&quot;40%&quot;&gt;&lt;/p&gt;
&lt;p&gt;或许是疫情的余温，近两年的经济形势非常差，对我的工作也产生了深远的影响，去年 OpenAI 发布了 ChatGPT 🤖，我们公司不久也启动了若干 AI 相关的项目，我有幸（or not?…doesn’t matter）参与了其中一个，从那之后直到现在，漂泊的生活便没有停止过，我记忆中的 27 岁似乎比 26 岁更加漫长。&lt;/p&gt;
    
    </summary>
    
      <category term="生活" scheme="https://cs-tao.github.io/blog/categories/%E7%94%9F%E6%B4%BB/"/>
    
    
      <category term="others" scheme="https://cs-tao.github.io/blog/tags/others/"/>
    
  </entry>
  
  <entry>
    <title>第一次开车回家</title>
    <link href="https://cs-tao.github.io/blog/%E7%94%9F%E6%B4%BB/%E7%AC%AC%E4%B8%80%E6%AC%A1%E5%BC%80%E8%BD%A6%E5%9B%9E%E5%AE%B6/"/>
    <id>https://cs-tao.github.io/blog/生活/第一次开车回家/</id>
    <published>2024-09-24T06:11:20.000Z</published>
    <updated>2025-01-28T04:15:07.206Z</updated>
    
    <content type="html"><![CDATA[<p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/20240206/27.jpg" width="40%" height="40%"></p><p>去年春节前的武汉很冷 🥶，伴随而来的还有百年难得一遇的冻雨 🌨️，而我却准备在这最困难的时候，开车从武汉回家 🚗。现在回想起来，也是一段非常奇妙的旅程，所以想着把这段经历记录一下。</p><a id="more"></a><hr><p>大年廿三晚上和土哥参加完实验室年终聚会回到家，发现楼下电动车上已经裹上了薄薄的一层冰，天上也持续地下着小雨。</p><p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/20240206/1.jpg" width="40%" height="40%"></p><p>不出所料，第二天整个武汉便被冰层覆盖，公司附近的树被压断了不少。当时看了下最近几天的天气预报，只有大年廿六天气稍好，遂决定廿六号离开武汉。</p><p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/20240206/13.jpg" width="40%" height="40%"></p><p>原计划开两天车回家，第一天冒着冻雨开到荆州，第二天从荆州开回家（彭水），但计划总是赶不上变化…</p><p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/20240206/16.jpg" width="20%" height="20%"></p><h3 id="🗓️-大年廿六"><a href="#🗓️-大年廿六" class="headerlink" title="🗓️ 大年廿六"></a>🗓️ 大年廿六</h3><p>下午两点在保利充满电，没想到仅出武汉就花了三小时。上高速后走走停停，天色逐渐暗了下来，快到仙桃服务区的时候，终于堵住不动了。</p><p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/20240206/17.jpg" width="40%" height="40%"></p><p>道路两侧积了很厚的雪，天上也开始零星下起了小雪，这个时候拿出了我在楼下买的的烤馍和泡面，以及出发前烧好的开水，准备吃个晚饭。不过没想到水温不够，泡不开泡面…</p><p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/20240206/19.jpg" width="40%" height="40%"></p><p>又等了一会，快到 9 点了，还堵在路上，不得已把车开到了仙桃服务区，顺便在服务区吃了晚饭。仙桃服务区很大，环境也很好，想着后面一定要带琦姐来一次。</p><p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/20240206/18.jpg" width="40%" height="40%"></p><p>没想到第一天是在仙桃服务区度过的，离目标荆州还有好几个小时的路程呢，不过还好小特的空调很给力，在驾驶位安稳地睡了一觉。</p><p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/20240206/35.jpg" width="20%" height="20%"></p><h3 id="🗓️-大年廿七"><a href="#🗓️-大年廿七" class="headerlink" title="🗓️ 大年廿七"></a>🗓️ 大年廿七</h3><p>今天起了个大早，车外还是零星地下着小雪，但已经是白茫茫的一片，在服务区吃了个早饭，顺便把车的电充满了，满血出发，今天的目标是宜昌！</p><p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/20240206/22.jpg" width="40%" height="40%"></p><p>刚出服务区，导航显示前方有些拥堵，等一会发现只是有两辆龟速车在缓慢前行，超过他们后又往前开了一会，这时导航显示前方非常拥堵。</p><p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/20240206/23.jpg" width="20%" height="20%"></p><p>果然没过一会彻底堵住了，能看到前方大概 500 米处有凿冰的车在以 10km/h 的速度开路，就这样一直堵到了中午，车外的雪也越下越大。</p><p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/20240206/25.jpg" width="40%" height="40%"></p><p>好在又过了不久终于放行了，浩浩荡荡的车队就这样一直往前开，大概十分钟后整个车队居然被赶下了高速…高速封路了，只能走国道，国道的道路状况比高速更复杂，因为两侧积雪的缘故，原本的双车道只剩下一个车身宽度的车道可走，只能跟在大货车后面缓慢通行。</p><p>出乎意料的是，国道的风景非常好，整个世界被白雪和云层覆盖，大地和天空交融在一块，是那种一望无际银装素裹的感觉。从仙桃到荆州，走了国道县道乡道，途径了很多乡镇，从热闹的氛围中已经能提前感受到年味，更加归心似箭了。</p><blockquote><p>只顾欣赏风景去了，没有想起拍照…</p></blockquote><p>进入荆州的时候一直很堵，查了一下才知道荆州是本次冻雨的重灾区，市区的积雪和我地盘差不多厚，一路上只能沿着前方车辆压过的车轱辘印行驶。途中还因为没有车轱辘印了，不得原路倒车，换了条路。好在运气不算很差，兜兜转转后还是从荆州收费站重新上了高速。（后面我和琦姐说，荆州那一段路我仿佛在越野…）</p><p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/20240206/34.jpg" width="40%" height="40%"></p><p>从荆州去宜昌的路上，路边的积雪越来越少，路况也越来越好，很快便到了宜昌市。找桃子问了下宜昌比较适合吃的地方，决定今晚就在宜昌好好玩一下！</p><h3 id="🗓️-大年廿七-晚"><a href="#🗓️-大年廿七-晚" class="headerlink" title="🗓️ 大年廿七-晚"></a>🗓️ 大年廿七-晚</h3><p>桃子说「陶珠路」上美食很多，建议我去这边玩一下，于是我便来到「陶珠路」。这是一条非常热闹的街，路边有很多夜宵店，整条街都弥漫着火锅的香味，每个店门口都有一位负责拉客的服务员。</p><p>听桃子建议找到了「北门老刘」，点了一份宜昌红油面和一份凉拌猪耳，感觉味道不错，就是油太重了，不是我喜欢吃的类型，喜欢吃襄阳牛肉面的朋友可以去尝试一下。</p><p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/20240206/29.jpg" width="40%" height="40%"></p><p>吃完晚饭，略感困倦，找了个能充电的酒店地下车库休息，今晚依旧住在车里，过年期间酒店的人很少，也很安静，今天就到此为止了，明天直接回家！</p><h3 id="🗓️-大年廿八"><a href="#🗓️-大年廿八" class="headerlink" title="🗓️ 大年廿八"></a>🗓️ 大年廿八</h3><p>从宜昌市区到高速需要开很久，会爬很高的盘山公路，路过了不少弯弯绕绕的乡镇式道路，但不得不说，宜昌的风景真的很好，清晨的朝阳照在山间，雾气渐渐消散，是工作后难得一见的景色。</p><blockquote><p>总是会忘记拍照…</p></blockquote><p>今天的第一站要先到恩施充个电，宜昌到恩施的路上非常顺利，G50 高速路况很好，中午 11 点左右便到了恩施服务区，充个电吃个饭，继续出发 🚀。</p><p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/20240206/32.jpg" width="20%" height="20%"></p><p>恩施到彭水的路上隧道很多，雾气很重，每个山头 🏔️ 都萦绕着一团雾，有的雾气甚至笼罩了隧道口，视野很差，需要打开雾灯才能继续行驶。</p><blockquote><p>不过在听到「前方 2 公里，靠右下高速，往保家收费站方向」的时候，感觉这一路的艰辛都是值得的。</p></blockquote><p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/20240206/36.jpg" width="32%" height="32%"></p><p>从保家收费站往彭水城区开，经过了很多记忆中有些模糊的道路，想起小时候一个人在这条路上骑自行车 🚴 的场景，不得感叹人终究是会长大的。</p><p>这趟「自驾游」开了三天，途径了很多城市，看到了很多平时见不到的风景，运气也很好，没有遇到因为冻雨堵车导致电量耗尽 🪫 的情况，最终平安回到家。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;http://home.cs-tao.cc/github-content/contents/blog/image/20240206/27.jpg&quot; width=&quot;40%&quot; height=&quot;40%&quot;&gt;&lt;/p&gt;
&lt;p&gt;去年春节前的武汉很冷 🥶，伴随而来的还有百年难得一遇的冻雨 🌨️，而我却准备在这最困难的时候，开车从武汉回家 🚗。现在回想起来，也是一段非常奇妙的旅程，所以想着把这段经历记录一下。&lt;/p&gt;
    
    </summary>
    
      <category term="生活" scheme="https://cs-tao.github.io/blog/categories/%E7%94%9F%E6%B4%BB/"/>
    
    
      <category term="others" scheme="https://cs-tao.github.io/blog/tags/others/"/>
    
  </entry>
  
  <entry>
    <title>如何基于 TS 类型系统实现五子棋</title>
    <link href="https://cs-tao.github.io/blog/%E6%8A%80%E6%9C%AF/%E5%A6%82%E4%BD%95%E5%9F%BA%E4%BA%8E-TS-%E7%B1%BB%E5%9E%8B%E7%B3%BB%E7%BB%9F%E5%AE%9E%E7%8E%B0%E4%BA%94%E5%AD%90%E6%A3%8B/"/>
    <id>https://cs-tao.github.io/blog/技术/如何基于-TS-类型系统实现五子棋/</id>
    <published>2024-09-11T03:51:20.000Z</published>
    <updated>2025-01-28T04:15:07.206Z</updated>
    
    <content type="html"><![CDATA[<p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/fir/00.jpeg" width="40%" height="40%"></p><p>TypeScript 是一个类型系统，如何实现五子棋应用呢？这不得不提到 TypeScript 的类型系统其实是图灵完备的 🤓</p><a id="more"></a><h1 id="能否实现"><a href="#能否实现" class="headerlink" title="能否实现"></a>能否实现</h1><h2 id="什么是图灵完备"><a href="#什么是图灵完备" class="headerlink" title="什么是图灵完备"></a>什么是图灵完备</h2><blockquote><p>图灵完备性是计算机科学中的一个概念，描述了一个系统在时间和资源充足的情况下，能够执行任何可以用算法表达的计算。这个概念以数学家阿兰·图灵的名字命名，图灵完备的系统能够模拟图灵机，后者是一个理论计算模型。这个特性对于编程语言至关重要，因为它意味着这些语言可以解决任何图灵机能够解决的问题，前提是没有内存或处理能力的限制。现代编程语言如 Python、Java 和 C++等都是图灵完备的例子。理解图灵完备性有助于评估不同编程范式的计算能力和局限性。</p></blockquote><h2 id="证明-TS-是图灵完备的"><a href="#证明-TS-是图灵完备的" class="headerlink" title="证明 TS 是图灵完备的"></a>证明 TS 是图灵完备的</h2><p>如果某个系统是图灵完备的，那么它一定能实现以下三个基础函数和三个基础操作</p><p><strong>三个基础函数</strong><br>常零函数：给定任意参数，返回值为零<br>后继函数：给定参数 X，返回值为 X + 1<br>映射函数：给定参数列表，返回指定位置的参数</p><p><strong>三个基础操作</strong><br>函数组合：将一个函数的返回值作为另一个函数的输入<br>原始递归函数：实现递归函数，包含基础条件的判断和递归条件的判断<br>极小化：找到某个函数返回值首次为 0 时的参数值<br>可以看到证明某个系统是否是图灵完备的比较复杂，但如果它能实现任意「偏递归函数」，那么它一定是图灵完备的，一个典型的偏递归函数的例子是除法运算。</p><p><strong>先上理论：</strong><br>什么是「偏递归函数」：这不得不提到与之对应的「全递归函数」，「全递归函数」指对于一个原始递归函数，任意输入都有对应的输出，而「偏递归函数」不一定能得到输出结果。<br>比如乘法是一个「全递归函数」，除法是一个「偏递归函数」（除数为 0 时无法输出结果）。这也就是为什么说能实现除法，系统就基本可以算是图灵完备了（当然这只是说「基本可以」，比如计算器可以实现除法，但它并不图灵完备）。</p><div align="center"><img src="http://home.cs-tao.cc/github-content/contents/blog/image/fir/01.svg" width="80%" height="80%"></div><p><strong>实际操作一下，如何通过 TS 类型系统实现除法操作：</strong></p><ol><li>定义三个基础方法和计算操作</li></ol><figure class="highlight typescript"><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"><span class="keyword">export</span> <span class="keyword">type</span> 不可能 = never;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 是 = <span class="literal">true</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 否 = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 1. 常零函数</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 零 = &#123;</span><br><span class="line">  是零吗: 是;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 正数 = &#123; 前一个数: 整数; 是零吗: 否 &#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 整数 = 零 | 正数;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 后继函数</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 加一&lt;T <span class="keyword">extends</span> 整数&gt; = &#123;</span><br><span class="line">  前一个数: T;</span><br><span class="line">  是零吗: 否;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. 投影函数，TS 自带，比如: type A_3_2&lt;T1, T2, T3&gt; = T2</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 1. 函数组合，TS 自带，比如: type A = B&lt;C&lt;number&gt;&gt;</span></span><br><span class="line"><span class="comment">// 2. 全递归函数，TS 可以通过 extends 语法实现条件判断和递归调用</span></span><br><span class="line"><span class="comment">// 3. 极小化，TS 可以通过 extends 和 infer 语法判断计算结果是否为零</span></span><br></pre></td></tr></table></figure><ol start="2"><li>实例化一些整数<br>其实 TS 通过 <code>type 三 = [1, 2, 3][&#39;length&#39;]</code> 可以直接拿到数字类型，我们这里再原始一点，通过数据结构来定义</li></ol><figure class="highlight typescript"><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"><span class="keyword">export</span> <span class="keyword">type</span> 一 = 加一&lt;零&gt;;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 二 = 加一&lt;一&gt;;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 三 = 加一&lt;二&gt;;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 四 = 加一&lt;三&gt;;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 五 = 加一&lt;四&gt;;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 六 = 加一&lt;五&gt;;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 七 = 加一&lt;六&gt;;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 八 = 加一&lt;七&gt;;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 九 = 加一&lt;八&gt;;</span><br></pre></td></tr></table></figure><ol start="3"><li>定义基础的比较运算</li></ol><figure class="highlight typescript"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 相等&lt;</span><br><span class="line">  第一个数 <span class="keyword">extends</span> 整数,</span><br><span class="line">  第二个数 <span class="keyword">extends</span> 整数</span><br><span class="line">&gt; = 第一个数 <span class="keyword">extends</span> 零</span><br><span class="line">  ? 第二个数 <span class="keyword">extends</span> 零</span><br><span class="line">    ? 是</span><br><span class="line">    : 否</span><br><span class="line">  : 第二个数 <span class="keyword">extends</span> 零</span><br><span class="line">  ? 否</span><br><span class="line">  : 相等&lt;减一&lt;第一个数&gt;, 减一&lt;第二个数&gt;&gt;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 不相等&lt;第一个数 <span class="keyword">extends</span> 整数, 第二个数 <span class="keyword">extends</span> 整数&gt; = 相等&lt;</span><br><span class="line">  第一个数,</span><br><span class="line">  第二个数</span><br><span class="line">&gt; <span class="keyword">extends</span> 是</span><br><span class="line">  ? 否</span><br><span class="line">  : 是;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 大于&lt;</span><br><span class="line">  第一个数 <span class="keyword">extends</span> 整数,</span><br><span class="line">  第二个数 <span class="keyword">extends</span> 整数</span><br><span class="line">&gt; = 第一个数 <span class="keyword">extends</span> 零</span><br><span class="line">  ? 否</span><br><span class="line">  : 第二个数 <span class="keyword">extends</span> 零</span><br><span class="line">  ? 是</span><br><span class="line">  : 大于&lt;减一&lt;第一个数&gt;, 减一&lt;第二个数&gt;&gt;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 小于&lt;</span><br><span class="line">  第一个数 <span class="keyword">extends</span> 整数,</span><br><span class="line">  第二个数 <span class="keyword">extends</span> 整数</span><br><span class="line">&gt; = 第一个数 <span class="keyword">extends</span> 零</span><br><span class="line">  ? 第二个数 <span class="keyword">extends</span> 零</span><br><span class="line">    ? 否</span><br><span class="line">    : 是</span><br><span class="line">  : 第二个数 <span class="keyword">extends</span> 零</span><br><span class="line">  ? 否</span><br><span class="line">  : 小于&lt;减一&lt;第一个数&gt;, 减一&lt;第二个数&gt;&gt;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 大于等于&lt;</span><br><span class="line">  第一个数 <span class="keyword">extends</span> 整数,</span><br><span class="line">  第二个数 <span class="keyword">extends</span> 整数</span><br><span class="line">&gt; = 是 <span class="keyword">extends</span> 相等&lt;第一个数, 第二个数&gt; ? 是 : 大于&lt;第一个数, 第二个数&gt;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 小于等于&lt;</span><br><span class="line">  第一个数 <span class="keyword">extends</span> 整数,</span><br><span class="line">  第二个数 <span class="keyword">extends</span> 整数</span><br><span class="line">&gt; = 是 <span class="keyword">extends</span> 相等&lt;第一个数, 第二个数&gt; ? 是 : 小于&lt;第一个数, 第二个数&gt;;</span><br></pre></td></tr></table></figure><ol start="4"><li>定义加、减、乘。这三个函数都是全递归函数</li></ol><figure class="highlight typescript"><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"><span class="keyword">export</span> <span class="keyword">type</span> 加法&lt;数字一 <span class="keyword">extends</span> 整数, 数字二 <span class="keyword">extends</span> 整数&gt; = 数字二 <span class="keyword">extends</span> 零</span><br><span class="line">  ? 数字一</span><br><span class="line">  : 加法&lt;加一&lt;数字一&gt;, 减一&lt;数字二&gt;&gt;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 减法&lt;数字一 <span class="keyword">extends</span> 整数, 数字二 <span class="keyword">extends</span> 整数&gt; = 数字二 <span class="keyword">extends</span> 零</span><br><span class="line">  ? 数字一</span><br><span class="line">  : 减法&lt;减一&lt;数字一&gt;, 减一&lt;数字二&gt;&gt;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 乘法&lt;数字一 <span class="keyword">extends</span> 整数, 数字二 <span class="keyword">extends</span> 整数&gt; = 数字二 <span class="keyword">extends</span> 零</span><br><span class="line">  ? 零</span><br><span class="line">  : 加法&lt;数字一, 乘法&lt;数字一, 减一&lt;数字二&gt;&gt;&gt;;</span><br></pre></td></tr></table></figure><ol start="5"><li>定义除法。可以看到除法的定义和减法的定义几乎一致，但通过后面的测试用例我们可以看到当除数为 0 时，代码会陷入计算的死循环中，这也是偏递归函数相比于全递归函数不同的地方</li></ol><figure class="highlight typescript"><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"><span class="keyword">export</span> <span class="keyword">type</span> 除法&lt;</span><br><span class="line">  数字一 <span class="keyword">extends</span> 整数,</span><br><span class="line">  数字二 <span class="keyword">extends</span> 整数,</span><br><span class="line">  结果 <span class="keyword">extends</span> 整数 = 零</span><br><span class="line">&gt; = 是 <span class="keyword">extends</span> 小于等于&lt;数字一, 数字二&gt;</span><br><span class="line">  ? 结果</span><br><span class="line">  : 除法&lt;减法&lt;数字一, 数字二&gt;, 数字二, 加一&lt;结果&gt;&gt;;</span><br></pre></td></tr></table></figure><ol start="6"><li>测试用例</li></ol><figure class="highlight typescript"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> Case1 = 相等&lt;加法&lt;一, 一&gt;, 二&gt;; <span class="comment">// true</span></span><br><span class="line"><span class="keyword">type</span> Case2 = 相等&lt;减法&lt;五, 三&gt;, 二&gt;; <span class="comment">// true</span></span><br><span class="line"><span class="keyword">type</span> Case3 = 相等&lt;乘法&lt;三, 三&gt;, 九&gt;; <span class="comment">// true</span></span><br><span class="line"><span class="keyword">type</span> Case4 = 相等&lt;除法&lt;八, 三&gt;, 二&gt;; <span class="comment">// true</span></span><br><span class="line"><span class="comment">// @ts-expect-error</span></span><br><span class="line"><span class="keyword">type</span> Case5 = 相等&lt;除法&lt;八, 零&gt;, 二&gt;; <span class="comment">// Type instantiation is excessively deep and possibly infinite.(2589)</span></span><br></pre></td></tr></table></figure><blockquote><p>完整代码见 TypeScript Playground</p></blockquote><h2 id="图灵完备能做什么"><a href="#图灵完备能做什么" class="headerlink" title="图灵完备能做什么"></a>图灵完备能做什么</h2><p>正如图灵完备的定义，如果一个系统是图灵完备的，那么它可以用来解决任何可计算的问题</p><p><strong>可计算问题</strong>：是否存在一个算法，能解决在任何输入下的此计算问题，如果存在，这个问题便是可计算的<br>一个不可计算问题的例子（判断某个函数是否死循环）：</p><blockquote><p>停机问题：给定一段程序的描述和该程序的一个有效输入，运行此程序，那么程序最终是会终止，还是会死循环下去？</p></blockquote><p>显然，五子棋程序是一个可计算问题：无论是用户落子后应该如何渲染，还是用户落子后对局是否结束，都是能够被计算的<br>理论成立，开始实现 🧑‍💻</p><h1 id="怎么实现"><a href="#怎么实现" class="headerlink" title="怎么实现"></a>怎么实现</h1><p>我们不难发现五子棋其实就是一个<strong>二维链表</strong>，链表的每一项是一个棋子或者为空，每落一子便得到一个新的二维链表。<br>那么通过 TS 类型系统实现五子棋这个问题便转化为了如何通过 TS 类型系统表示一个二维链表类型，并实现基于当前的二维链表和落子位置，推导出新的二维链表类型，最后基于二维链表类型实现棋盘渲染和胜负判定</p><div align="center"><img src="http://home.cs-tao.cc/github-content/contents/blog/image/fir/02.svg" width="80%" height="80%"></div><h2 id="定义棋子"><a href="#定义棋子" class="headerlink" title="定义棋子"></a>定义棋子</h2><p>棋子类型能够区分黑白即可</p><blockquote><p>大多数 TS 编辑器都是偏黑色或偏白色，所以这里使用红绿作为棋子颜色</p></blockquote><figure class="highlight typescript"><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"><span class="keyword">type</span> 黑色符号 = <span class="string">"🔴"</span>;</span><br><span class="line"><span class="keyword">type</span> 白色符号 = <span class="string">"🟢"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> 黑色 = <span class="string">"黑"</span>;</span><br><span class="line"><span class="keyword">type</span> 白色 = <span class="string">"白"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> 颜色 = 黑色 | 白色;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> 棋子&lt;T <span class="keyword">extends</span> 颜色&gt; = &#123;</span><br><span class="line">  符号: T <span class="keyword">extends</span> 黑色 ? 黑色符号 : 白色符号;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h2 id="定义棋盘"><a href="#定义棋盘" class="headerlink" title="定义棋盘"></a>定义棋盘</h2><p>棋盘是一个以棋盘格为单元组成的二维链表，链表的第一个纬度可表示棋盘的一行，那么我们可以先定义出棋盘格，再定义棋盘行，最后定义出棋盘</p><h3 id="定义棋盘格"><a href="#定义棋盘格" class="headerlink" title="定义棋盘格"></a>定义棋盘格</h3><p>棋盘格需要有<strong>坐标信息</strong>和当前位置<strong>棋子</strong>的信息<br>对于坐标，可以使用上文中已经定义出的整数类型实现</p><figure class="highlight typescript"><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"><span class="keyword">type</span> 棋子横坐标 = 一 | 二 | 三 | 四 | 五 | 六 | 七 | 八 | 九;</span><br><span class="line"><span class="keyword">type</span> 棋子纵坐标 = 一 | 二 | 三 | 四 | 五 | 六 | 七 | 八 | 九;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> 棋子坐标 = &#123;</span><br><span class="line">  横: 棋子横坐标;</span><br><span class="line">  纵: 棋子纵坐标;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>进一步得到棋盘格的定义</p><figure class="highlight typescript"><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 class="keyword">export</span> <span class="keyword">type</span> 棋盘格 = &#123;</span><br><span class="line">  内容: 棋子&lt;颜色&gt; | 空;</span><br><span class="line">  上一格: 棋盘格 | 空;</span><br><span class="line">  下一格: 棋盘格 | 空;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>可以看到这里定义棋盘格时便声明了<strong>双向链表结构</strong>，每一个棋盘格都记录着它前后棋盘格的引用，便于后续的胜负判定</p><h3 id="定义棋盘行"><a href="#定义棋盘行" class="headerlink" title="定义棋盘行"></a>定义棋盘行</h3><p>棋盘行的定义和棋盘格类似</p><figure class="highlight typescript"><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 class="keyword">export</span> <span class="keyword">type</span> 棋盘行 = &#123;</span><br><span class="line">  内容: 棋盘格;</span><br><span class="line">  上一行: 棋盘行 | 空;</span><br><span class="line">  下一行: 棋盘行 | 空;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h3 id="定义整个棋盘"><a href="#定义整个棋盘" class="headerlink" title="定义整个棋盘"></a>定义整个棋盘</h3><p>棋盘的定义也是依葫芦画瓢</p><figure class="highlight typescript"><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 class="keyword">export</span> <span class="keyword">type</span> 棋盘 = &#123;</span><br><span class="line">  内容: 棋盘行;</span><br><span class="line">  待落颜色: 颜色;</span><br><span class="line">  结果: 空 | 颜色;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>我在定义棋盘时同时定义了当前待落子的颜色信息和当前结果，便于后续渲染棋盘时提示该哪一方落子以及对局是否结束</p><h2 id="构造棋盘"><a href="#构造棋盘" class="headerlink" title="构造棋盘"></a>构造棋盘</h2><p>棋盘的构造也就是通过参数生成棋盘类型，类似的棋盘行和棋盘格也需要构造，以构造棋盘格为例：</p><figure class="highlight typescript"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 构造棋盘格参数 = &#123;</span><br><span class="line">  内容: 棋盘格[<span class="string">"内容"</span>];</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 构造棋盘格&lt;</span><br><span class="line">  T <span class="keyword">extends</span> 构造棋盘格参数,</span><br><span class="line">  P <span class="keyword">extends</span> 棋盘格[<span class="string">"上一格"</span>],</span><br><span class="line">  K <span class="keyword">extends</span> 棋盘格[<span class="string">"下一格"</span>]</span><br><span class="line">&gt; = &#123;</span><br><span class="line">  内容: T[<span class="string">"内容"</span>];</span><br><span class="line">  上一格: P;</span><br><span class="line">  下一格: K;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>构造棋盘行的方法类似，这里省略，我们直接看棋盘是如何构造的</p><figure class="highlight typescript"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 构造棋盘参数 = &#123;</span><br><span class="line">  内容: 棋盘行;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 构造棋盘&lt;</span><br><span class="line">  T <span class="keyword">extends</span> 构造棋盘参数,</span><br><span class="line">  P <span class="keyword">extends</span> 颜色,</span><br><span class="line">  K <span class="keyword">extends</span> 空 | 颜色</span><br><span class="line">&gt; = &#123;</span><br><span class="line">  内容: T[<span class="string">"内容"</span>];</span><br><span class="line">  待落颜色: P;</span><br><span class="line">  结果: K;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>如何构造五子棋盘呢，以 9*9 的棋盘为例，我们可以先定义棋盘格，由棋盘格构造棋盘行，由棋盘行得到目标棋盘</p><blockquote><p>这里没有使用递归构造，TS 有最大递归次数限制；如果在这里使用递归构造，后面计算结果时会突破上限</p></blockquote><figure class="highlight typescript"><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><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> 构造五子棋棋盘行参数 = [</span><br><span class="line">  构造棋盘格参数,</span><br><span class="line">  构造棋盘格参数,</span><br><span class="line">  构造棋盘格参数,</span><br><span class="line">  构造棋盘格参数,</span><br><span class="line">  构造棋盘格参数,</span><br><span class="line">  构造棋盘格参数,</span><br><span class="line">  构造棋盘格参数,</span><br><span class="line">  构造棋盘格参数,</span><br><span class="line">  构造棋盘格参数</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> 构造五子棋棋盘行&lt;</span><br><span class="line">  T <span class="keyword">extends</span> 构造五子棋棋盘行参数,</span><br><span class="line">  P <span class="keyword">extends</span> 棋盘行[<span class="string">"下一行"</span>]</span><br><span class="line">&gt; = T <span class="keyword">extends</span> [</span><br><span class="line">  infer 第一格 <span class="keyword">extends</span> 构造棋盘格参数,</span><br><span class="line">  infer 第二格 <span class="keyword">extends</span> 构造棋盘格参数,</span><br><span class="line">  infer 第三格 <span class="keyword">extends</span> 构造棋盘格参数,</span><br><span class="line">  infer 第四格 <span class="keyword">extends</span> 构造棋盘格参数,</span><br><span class="line">  infer 第五格 <span class="keyword">extends</span> 构造棋盘格参数,</span><br><span class="line">  infer 第六格 <span class="keyword">extends</span> 构造棋盘格参数,</span><br><span class="line">  infer 第七格 <span class="keyword">extends</span> 构造棋盘格参数,</span><br><span class="line">  infer 第八格 <span class="keyword">extends</span> 构造棋盘格参数,</span><br><span class="line">  infer 第九格 <span class="keyword">extends</span> 构造棋盘格参数</span><br><span class="line">]</span><br><span class="line">  ? 构造棋盘行&lt;</span><br><span class="line">      &#123;</span><br><span class="line">        内容: 构造棋盘格&lt;</span><br><span class="line">          第一格,</span><br><span class="line">          空,</span><br><span class="line">          构造棋盘格&lt;</span><br><span class="line">            第二格,</span><br><span class="line">            空,</span><br><span class="line">            构造棋盘格&lt;</span><br><span class="line">              第三格,</span><br><span class="line">              空,</span><br><span class="line">              构造棋盘格&lt;</span><br><span class="line">                第四格,</span><br><span class="line">                空,</span><br><span class="line">                构造棋盘格&lt;</span><br><span class="line">                  第五格,</span><br><span class="line">                  空,</span><br><span class="line">                  构造棋盘格&lt;</span><br><span class="line">                    第六格,</span><br><span class="line">                    空,</span><br><span class="line">                    构造棋盘格&lt;</span><br><span class="line">                      第七格,</span><br><span class="line">                      空,</span><br><span class="line">                      构造棋盘格&lt;第八格, 空, 构造棋盘格&lt;第九格, 空, 空&gt;&gt;</span><br><span class="line">                    &gt;</span><br><span class="line">                  &gt;</span><br><span class="line">                &gt;</span><br><span class="line">              &gt;</span><br><span class="line">            &gt;</span><br><span class="line">          &gt;</span><br><span class="line">        &gt;;</span><br><span class="line">      &#125;,</span><br><span class="line">      空,</span><br><span class="line">      P</span><br><span class="line">    &gt;</span><br><span class="line">  : 不可能;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> 构造五子棋棋盘参数 = [</span><br><span class="line">  构造五子棋棋盘行参数,</span><br><span class="line">  构造五子棋棋盘行参数,</span><br><span class="line">  构造五子棋棋盘行参数,</span><br><span class="line">  构造五子棋棋盘行参数,</span><br><span class="line">  构造五子棋棋盘行参数,</span><br><span class="line">  构造五子棋棋盘行参数,</span><br><span class="line">  构造五子棋棋盘行参数,</span><br><span class="line">  构造五子棋棋盘行参数,</span><br><span class="line">  构造五子棋棋盘行参数</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> 构造五子棋棋盘&lt;T <span class="keyword">extends</span> 构造五子棋棋盘参数&gt; = T <span class="keyword">extends</span> [</span><br><span class="line">  infer 第一行 <span class="keyword">extends</span> 构造五子棋棋盘行参数,</span><br><span class="line">  infer 第二行 <span class="keyword">extends</span> 构造五子棋棋盘行参数,</span><br><span class="line">  infer 第三行 <span class="keyword">extends</span> 构造五子棋棋盘行参数,</span><br><span class="line">  infer 第四行 <span class="keyword">extends</span> 构造五子棋棋盘行参数,</span><br><span class="line">  infer 第五行 <span class="keyword">extends</span> 构造五子棋棋盘行参数,</span><br><span class="line">  infer 第六行 <span class="keyword">extends</span> 构造五子棋棋盘行参数,</span><br><span class="line">  infer 第七行 <span class="keyword">extends</span> 构造五子棋棋盘行参数,</span><br><span class="line">  infer 第八行 <span class="keyword">extends</span> 构造五子棋棋盘行参数,</span><br><span class="line">  infer 第九行 <span class="keyword">extends</span> 构造五子棋棋盘行参数</span><br><span class="line">]</span><br><span class="line">  ? 构造棋盘&lt;</span><br><span class="line">      &#123;</span><br><span class="line">        内容: 构造五子棋棋盘行&lt;</span><br><span class="line">          第一行,</span><br><span class="line">          构造五子棋棋盘行&lt;</span><br><span class="line">            第二行,</span><br><span class="line">            构造五子棋棋盘行&lt;</span><br><span class="line">              第三行,</span><br><span class="line">              构造五子棋棋盘行&lt;</span><br><span class="line">                第四行,</span><br><span class="line">                构造五子棋棋盘行&lt;</span><br><span class="line">                  第五行,</span><br><span class="line">                  构造五子棋棋盘行&lt;</span><br><span class="line">                    第六行,</span><br><span class="line">                    构造五子棋棋盘行&lt;</span><br><span class="line">                      第七行,</span><br><span class="line">                      构造五子棋棋盘行&lt;第八行, 构造五子棋棋盘行&lt;第九行, 空&gt;&gt;</span><br><span class="line">                    &gt;</span><br><span class="line">                  &gt;</span><br><span class="line">                &gt;</span><br><span class="line">              &gt;</span><br><span class="line">            &gt;</span><br><span class="line">          &gt;</span><br><span class="line">        &gt;;</span><br><span class="line">      &#125;,</span><br><span class="line">      <span class="string">"黑"</span>,</span><br><span class="line">      空,</span><br><span class="line">      否</span><br><span class="line">    &gt;</span><br><span class="line">  : 不可能;</span><br></pre></td></tr></table></figure><h2 id="落子后生成新棋盘"><a href="#落子后生成新棋盘" class="headerlink" title="落子后生成新棋盘"></a>落子后生成新棋盘</h2><p>每落一子都需要生成一个新的棋盘，也就是说落子是基于当前棋盘和落子信息生成新的棋盘的函数。<br>基本原理就是通过落子参数，替换掉当前棋盘的某一个棋盘格后，生成新的棋盘</p><figure class="highlight typescript"><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"><span class="keyword">export</span> <span class="keyword">type</span> 落子&lt;</span><br><span class="line">  某棋盘 <span class="keyword">extends</span> 棋盘,</span><br><span class="line">  某坐标 <span class="keyword">extends</span> 棋子坐标,</span><br><span class="line">  某颜色 <span class="keyword">extends</span> 颜色</span><br><span class="line">&gt; = 获取棋盘某位置的单元&lt;某棋盘, 某坐标&gt; <span class="keyword">extends</span> infer 当前格</span><br><span class="line">  ? 当前格 <span class="keyword">extends</span> 棋盘格</span><br><span class="line">    ? 当前格[<span class="string">"内容"</span>] <span class="keyword">extends</span> 空</span><br><span class="line">      ? 获取棋盘指定行&lt;某棋盘, 某坐标[<span class="string">"纵"</span>]&gt; <span class="keyword">extends</span> infer 当前行</span><br><span class="line">        ? 当前行 <span class="keyword">extends</span> 棋盘行</span><br><span class="line">          ? 替换棋盘某行&lt;</span><br><span class="line">              某棋盘,</span><br><span class="line">              某坐标[<span class="string">"纵"</span>],</span><br><span class="line">              从内容构造棋盘行&lt;</span><br><span class="line">                替换棋盘行某格&lt;当前行, 某坐标[<span class="string">"横"</span>], 棋子&lt;某颜色&gt;&gt;,</span><br><span class="line">                当前行[<span class="string">"上一行"</span>],</span><br><span class="line">                当前行[<span class="string">"下一行"</span>]</span><br><span class="line">              &gt;</span><br><span class="line">            &gt; <span class="keyword">extends</span> infer 某棋盘内容</span><br><span class="line">            ? 某棋盘内容 <span class="keyword">extends</span> 棋盘[<span class="string">"内容"</span>]</span><br><span class="line">              ? 构造棋盘&lt;</span><br><span class="line">                  &#123;</span><br><span class="line">                    内容: 某棋盘内容;</span><br><span class="line">                  &#125;,</span><br><span class="line">                  某棋盘[<span class="string">"待落颜色"</span>] <span class="keyword">extends</span> <span class="string">"黑"</span> ? <span class="string">"白"</span> : <span class="string">"黑"</span>,</span><br><span class="line">                  计算结果&lt;某棋盘内容&gt;</span><br><span class="line">                &gt;</span><br><span class="line">              : 不可能</span><br><span class="line">            : 不可能</span><br><span class="line">          : 不可能</span><br><span class="line">        : 不可能</span><br><span class="line">      : 错误</span><br><span class="line">    : 不可能</span><br><span class="line">  : 不可能;</span><br></pre></td></tr></table></figure><h2 id="计算结果"><a href="#计算结果" class="headerlink" title="计算结果"></a>计算结果</h2><p>计算结果的大概原理是遍历每一个棋盘格，计算其各方向同色子的个数是否达到五个，因为是每个棋子都计算一遍，所以只需要「向前遍历」即可</p><figure class="highlight typescript"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> 获胜方向 = <span class="string">"横行"</span> | <span class="string">"纵向"</span> | <span class="string">"正斜"</span> | <span class="string">"反斜"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> 根据方向获取下一坐标&lt;</span><br><span class="line">  当前坐标 <span class="keyword">extends</span> 棋子坐标,</span><br><span class="line">  方向 <span class="keyword">extends</span> 获胜方向</span><br><span class="line">&gt; = 方向 <span class="keyword">extends</span> <span class="string">"横行"</span></span><br><span class="line">  ? 构造棋子坐标&lt;加一&lt;当前坐标[<span class="string">"横"</span>]&gt;, 当前坐标[<span class="string">"纵"</span>]&gt;</span><br><span class="line">  : 方向 <span class="keyword">extends</span> <span class="string">"纵向"</span></span><br><span class="line">  ? 构造棋子坐标&lt;当前坐标[<span class="string">"横"</span>], 加一&lt;当前坐标[<span class="string">"纵"</span>]&gt;&gt;</span><br><span class="line">  : 方向 <span class="keyword">extends</span> <span class="string">"正斜"</span></span><br><span class="line">  ? 构造棋子坐标&lt;加一&lt;当前坐标[<span class="string">"横"</span>]&gt;, 加一&lt;当前坐标[<span class="string">"纵"</span>]&gt;&gt;</span><br><span class="line">  : 方向 <span class="keyword">extends</span> <span class="string">"反斜"</span></span><br><span class="line">  ? 构造棋子坐标&lt;减一&lt;当前坐标[<span class="string">"横"</span>]&gt;, 加一&lt;当前坐标[<span class="string">"纵"</span>]&gt;&gt;</span><br><span class="line">  : 不可能;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> 某向是否胜利&lt;</span><br><span class="line">  方向 <span class="keyword">extends</span> 获胜方向,</span><br><span class="line">  某棋盘内容 <span class="keyword">extends</span> 棋盘[<span class="string">"内容"</span>],</span><br><span class="line">  某坐标 <span class="keyword">extends</span> 棋子坐标 | 空,</span><br><span class="line">  某棋盘格 <span class="keyword">extends</span> 棋盘格 | 空,</span><br><span class="line">  某颜色 <span class="keyword">extends</span> 颜色,</span><br><span class="line">  胜利所需个数 <span class="keyword">extends</span> 三 | 五,</span><br><span class="line">  迭代次数 <span class="keyword">extends</span> 正数 = 一</span><br><span class="line">&gt; = 某棋盘格 <span class="keyword">extends</span> 棋盘格</span><br><span class="line">  ? 某坐标 <span class="keyword">extends</span> 棋子坐标</span><br><span class="line">    ? 某棋盘格[<span class="string">"内容"</span>] <span class="keyword">extends</span> 棋子&lt;某颜色&gt;</span><br><span class="line">      ? 是 <span class="keyword">extends</span> 相等&lt;迭代次数, 胜利所需个数&gt;</span><br><span class="line">        ? 是</span><br><span class="line">        : 根据方向获取下一坐标&lt;某坐标, 方向&gt; <span class="keyword">extends</span> infer 下一坐标</span><br><span class="line">        ? 某向是否胜利&lt;</span><br><span class="line">            方向,</span><br><span class="line">            某棋盘内容,</span><br><span class="line">            下一坐标 <span class="keyword">extends</span> 棋子坐标 ? 下一坐标 : 空,</span><br><span class="line">            下一坐标 <span class="keyword">extends</span> 棋子坐标</span><br><span class="line">              ? 根据棋盘内容获取棋盘某位置的单元&lt;某棋盘内容, 下一坐标&gt;</span><br><span class="line">              : 空,</span><br><span class="line">            某颜色,</span><br><span class="line">            胜利所需个数,</span><br><span class="line">            加一&lt;迭代次数&gt;</span><br><span class="line">          &gt;</span><br><span class="line">        : 不可能</span><br><span class="line">      : 否</span><br><span class="line">    : 否</span><br><span class="line">  : 否;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> 是否胜利&lt;</span><br><span class="line">  某方向 <span class="keyword">extends</span> 获胜方向,</span><br><span class="line">  某棋盘内容 <span class="keyword">extends</span> 棋盘[<span class="string">"内容"</span>],</span><br><span class="line">  某坐标 <span class="keyword">extends</span> 棋子坐标,</span><br><span class="line">  某棋盘格 <span class="keyword">extends</span> 棋盘格,</span><br><span class="line">  某颜色 <span class="keyword">extends</span> 颜色,</span><br><span class="line">  胜利所需个数 <span class="keyword">extends</span> 三 | 五</span><br><span class="line">&gt; = 某方向 <span class="keyword">extends</span> 获胜方向</span><br><span class="line">  ? 某向是否胜利&lt;某方向, 某棋盘内容, 某坐标, 某棋盘格, 某颜色, 胜利所需个数&gt;</span><br><span class="line">  : 不可能;</span><br><span class="line"></span><br><span class="line"><span class="comment">// e.g. type 结果 = 是否胜利&lt;获胜方向, ...&gt; = 某向是否胜利&lt;"横行", ...&gt; | 某向是否胜利&lt;"纵向", ...&gt; | 某向是否胜利&lt;"正斜", ...&gt; | 某向是否胜利&lt;"反斜", ...&gt;</span></span><br></pre></td></tr></table></figure><blockquote><p>写到这里时突然想到五子棋的逻辑比较简单，其实只计算最后落子位置八个方向是否胜利即可，这样稍微麻烦的地方在于计算时不仅需要「向前遍历」，还需要「向后遍历」</p></blockquote><h2 id="渲染棋盘"><a href="#渲染棋盘" class="headerlink" title="渲染棋盘"></a>渲染棋盘</h2><p>对于不同的语言和不同的执行环境，渲染方式往往不一样，比如 HTML 的渲染是通过 DOM 和 CSSOM 在浏览器画布中实现的、命令行的渲染是通过普通字符和控制字符在终端实现的。TS 的渲染类似，可以借助于类型提示将字符渲染到 IDE 的提示弹窗里，就像这样：</p><p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/fir/03.png" width="40%" height="40%"></p><p>那么我们只需要实现一个渲染棋盘的类型，将棋盘作为参数即可</p><blockquote><p>其中用到了 TS 模版字符串的能力，这是 TS 能够展示多样化信息的关键，也是一个很强大的功能</p></blockquote><figure class="highlight typescript"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 空符号 = <span class="string">"➕"</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 元符号 = <span class="string">"💲"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> 五子棋元坐标 =</span><br><span class="line">  | 构造棋子坐标&lt;三, 三&gt;</span><br><span class="line">  | 构造棋子坐标&lt;三, 七&gt;</span><br><span class="line">  | 构造棋子坐标&lt;五, 五&gt;</span><br><span class="line">  | 构造棋子坐标&lt;七, 三&gt;</span><br><span class="line">  | 构造棋子坐标&lt;七, 七&gt;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 渲染空格&lt;</span><br><span class="line">  纵坐标 <span class="keyword">extends</span> 棋子纵坐标,</span><br><span class="line">  横坐标 <span class="keyword">extends</span> 棋子横坐标,</span><br><span class="line">  元坐标 <span class="keyword">extends</span> 棋子坐标</span><br><span class="line">&gt; = 构造棋子坐标&lt;横坐标, 纵坐标&gt; <span class="keyword">extends</span> 元坐标 ? 元符号 : 空符号;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 渲染格&lt;</span><br><span class="line">  某格 <span class="keyword">extends</span> 棋盘格,</span><br><span class="line">  纵坐标 <span class="keyword">extends</span> 棋子纵坐标,</span><br><span class="line">  横坐标 <span class="keyword">extends</span> 棋子横坐标,</span><br><span class="line">  元坐标 <span class="keyword">extends</span> 棋子坐标</span><br><span class="line">&gt; = 某格[<span class="string">"内容"</span>] <span class="keyword">extends</span> infer 待渲染棋子</span><br><span class="line">  ? 待渲染棋子 <span class="keyword">extends</span> 棋子&lt;颜色&gt;</span><br><span class="line">    ? 待渲染棋子 <span class="keyword">extends</span> 棋子&lt;黑色&gt;</span><br><span class="line">      ? 棋子&lt;黑色&gt;[<span class="string">"符号"</span>]</span><br><span class="line">      : 棋子&lt;白色&gt;[<span class="string">"符号"</span>]</span><br><span class="line">    : 待渲染棋子 <span class="keyword">extends</span> 空</span><br><span class="line">    ? 渲染空格&lt;横坐标, 纵坐标, 元坐标&gt;</span><br><span class="line">    : 不可能</span><br><span class="line">  : 不可能;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 拼接单元格&lt;</span><br><span class="line">  迭代结果 <span class="keyword">extends</span> <span class="built_in">string</span>,</span><br><span class="line">  当前结果 <span class="keyword">extends</span> <span class="built_in">string</span></span><br><span class="line">&gt; = 迭代结果 <span class="keyword">extends</span> <span class="string">""</span> ? 当前结果 : <span class="string">`<span class="subst">$&#123;迭代结果&#125;</span> <span class="subst">$&#123;当前结果&#125;</span>`</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 渲染行内容&lt;</span><br><span class="line">  某格 <span class="keyword">extends</span> 棋盘格 | 空,</span><br><span class="line">  元坐标 <span class="keyword">extends</span> 棋子坐标,</span><br><span class="line">  纵坐标 <span class="keyword">extends</span> 棋子纵坐标,</span><br><span class="line">  迭代结果 <span class="keyword">extends</span> <span class="built_in">string</span> = <span class="string">""</span>,</span><br><span class="line">  迭代号 <span class="keyword">extends</span> 整数 = 最小横坐标</span><br><span class="line">&gt; = 某格 <span class="keyword">extends</span> infer 当前格</span><br><span class="line">  ? 迭代号 <span class="keyword">extends</span> infer 横坐标</span><br><span class="line">    ? 当前格 <span class="keyword">extends</span> 棋盘格</span><br><span class="line">      ? 横坐标 <span class="keyword">extends</span> 棋子横坐标</span><br><span class="line">        ? 渲染行内容&lt;</span><br><span class="line">            当前格[<span class="string">"下一格"</span>],</span><br><span class="line">            元坐标,</span><br><span class="line">            纵坐标,</span><br><span class="line">            拼接单元格&lt;迭代结果, 渲染格&lt;当前格, 纵坐标, 横坐标, 元坐标&gt;&gt;,</span><br><span class="line">            加一&lt;迭代号&gt;</span><br><span class="line">          &gt;</span><br><span class="line">        : 迭代结果</span><br><span class="line">      : 当前格 <span class="keyword">extends</span> 空</span><br><span class="line">      ? 迭代结果</span><br><span class="line">      : 不可能</span><br><span class="line">    : 不可能</span><br><span class="line">  : 不可能;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> 渲染五子棋盘&lt;某棋盘 <span class="keyword">extends</span> 棋盘&gt; = &#123;</span><br><span class="line">  [key <span class="keyword">in</span></span><br><span class="line">    | <span class="string">"一"</span></span><br><span class="line">    | <span class="string">"二"</span></span><br><span class="line">    | <span class="string">"三"</span></span><br><span class="line">    | <span class="string">"四"</span></span><br><span class="line">    | <span class="string">"五"</span></span><br><span class="line">    | <span class="string">"六"</span></span><br><span class="line">    | <span class="string">"七"</span></span><br><span class="line">    | <span class="string">"八"</span></span><br><span class="line">    | <span class="string">"九"</span>]: key <span class="keyword">extends</span> infer 行</span><br><span class="line">    ? 行 <span class="keyword">extends</span> <span class="string">"上"</span></span><br><span class="line">      ? <span class="string">" ⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽ "</span></span><br><span class="line">      : 行 <span class="keyword">extends</span> <span class="string">"下"</span></span><br><span class="line">      ? <span class="string">" ⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺ "</span></span><br><span class="line">      : 行 <span class="keyword">extends</span> keyof 正数键值对</span><br><span class="line">      ? 获取棋盘指定行&lt;某棋盘, 正数键值对[行]&gt; <span class="keyword">extends</span> infer 某行</span><br><span class="line">        ? 某行 <span class="keyword">extends</span> 棋盘行</span><br><span class="line">          ? <span class="string">`⎪ <span class="subst">$&#123;渲染行内容&lt;某行["内容"], 五子棋元坐标, 正数键值对[行]&gt;&#125;</span> ⎪`</span></span><br><span class="line">          : 不可能</span><br><span class="line">        : 不可能</span><br><span class="line">      : 不可能</span><br><span class="line">    : 不可能;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h1 id="看看效果"><a href="#看看效果" class="headerlink" title="看看效果"></a>看看效果</h1><h2 id="落子和渲染"><a href="#落子和渲染" class="headerlink" title="落子和渲染"></a>落子和渲染</h2><figure class="highlight typescript"><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"><span class="keyword">import</span> &#123; 初始五子棋盘, 渲染五子棋盘, 落子 &#125; <span class="keyword">from</span> <span class="string">"./src"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> 初始结果 = 渲染五子棋盘&lt;初始五子棋盘&gt;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> 黑第一步 = 落子&lt;初始五子棋盘, <span class="string">"黑"</span>, <span class="string">"二"</span>, <span class="string">"三"</span>&gt;;</span><br><span class="line"><span class="keyword">type</span> 黑第一步结果 = 渲染五子棋盘&lt;黑第一步&gt;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> 白第一步 = 落子&lt;黑第一步, <span class="string">"白"</span>, <span class="string">"三"</span>, <span class="string">"三"</span>&gt;;</span><br><span class="line"><span class="keyword">type</span> 白第一步结果 = 渲染五子棋盘&lt;白第一步&gt;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> 黑第二步 = 落子&lt;白第一步, <span class="string">"黑"</span>, <span class="string">"三"</span>, <span class="string">"四"</span>&gt;;</span><br><span class="line"><span class="keyword">type</span> 黑第二步结果 = 渲染五子棋盘&lt;黑第二步&gt;;</span><br></pre></td></tr></table></figure><table><thead><tr><th style="text-align:center">初始棋盘</th><th style="text-align:center">黑棋第一步 (二, 三)</th></tr></thead><tbody><tr><td style="text-align:center"><img src="http://home.cs-tao.cc/github-content/contents/blog/image/fir/04-1.png" alt="初始棋盘.png"></td><td style="text-align:center"><img src="http://home.cs-tao.cc/github-content/contents/blog/image/fir/04-2.png" alt="黑棋第一步.png"></td></tr><tr><td style="text-align:center">白棋第一步 (三, 三)</td><td style="text-align:center">黑棋第二步 (三, 四)</td></tr><tr><td style="text-align:center"><img src="http://home.cs-tao.cc/github-content/contents/blog/image/fir/04-3.png" alt="白棋第一步.png"></td><td style="text-align:center"><img src="http://home.cs-tao.cc/github-content/contents/blog/image/fir/04-4.png" alt="黑棋第二步.png"></td></tr></tbody></table><h2 id="计算结果-1"><a href="#计算结果-1" class="headerlink" title="计算结果"></a>计算结果</h2><blockquote><p>「五子棋」计算结果时递归次数会超出 ts 限制，这里以「井字棋」为例</p></blockquote><figure class="highlight typescript"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; 初始井字棋盘, 渲染井字棋盘, 落子 &#125; <span class="keyword">from</span> <span class="string">"./src"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> 初始结果 = 渲染井字棋盘&lt;初始井字棋盘&gt;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> 黑第一步 = 落子&lt;初始井字棋盘, <span class="string">"黑"</span>, <span class="string">"一"</span>, <span class="string">"一"</span>&gt;;</span><br><span class="line"><span class="keyword">type</span> 黑第一步结果 = 渲染井字棋盘&lt;黑第一步&gt;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> 白第一步 = 落子&lt;黑第一步, <span class="string">"白"</span>, <span class="string">"三"</span>, <span class="string">"一"</span>&gt;;</span><br><span class="line"><span class="keyword">type</span> 白第一步结果 = 渲染井字棋盘&lt;白第一步&gt;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> 黑第二步 = 落子&lt;白第一步, <span class="string">"黑"</span>, <span class="string">"二"</span>, <span class="string">"二"</span>&gt;;</span><br><span class="line"><span class="keyword">type</span> 黑第二步结果 = 渲染井字棋盘&lt;黑第二步&gt;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> 白第二步 = 落子&lt;黑第二步, <span class="string">"白"</span>, <span class="string">"三"</span>, <span class="string">"二"</span>&gt;;</span><br><span class="line"><span class="keyword">type</span> 白第二步结果 = 渲染井字棋盘&lt;白第二步&gt;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> 黑第三步 = 落子&lt;白第二步, <span class="string">"黑"</span>, <span class="string">"三"</span>, <span class="string">"三"</span>&gt;;</span><br><span class="line"><span class="keyword">type</span> 黑第三步结果 = 渲染井字棋盘&lt;黑第三步&gt;;</span><br><span class="line"><span class="comment">// =&gt; &#123;</span></span><br><span class="line"><span class="comment">//   一: "⎪ 🔴 ➕ 🟢 ⎪";</span></span><br><span class="line"><span class="comment">//   二: "⎪ ➕ 🔴 🟢 ⎪";</span></span><br><span class="line"><span class="comment">//   三: "⎪ ➕ ➕ 🔴 ⎪";</span></span><br><span class="line"><span class="comment">//   获胜: "黑-🔴";</span></span><br><span class="line"><span class="comment">// &#125;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> 重复落子 = 落子&lt;白第一步, <span class="string">"黑"</span>, <span class="string">"三"</span>, <span class="string">"一"</span>&gt;;</span><br><span class="line"><span class="comment">// @ts-expect-error 重复落子: &#123; 出错: "原因: 当前位置「三, 一」已有棋子「🟢」"; &#125;</span></span><br><span class="line"><span class="keyword">type</span> 重复落子结果 = 渲染井字棋盘&lt;重复落子&gt;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> 不能继续落子 = 落子&lt;黑第三步, <span class="string">"白"</span>, <span class="string">"二"</span>, <span class="string">"三"</span>&gt;;</span><br><span class="line"><span class="comment">// @ts-expect-error 不能继续落子: &#123; 出错: "原因: 不能落子，「黑-🔴」已获胜"; &#125;</span></span><br><span class="line"><span class="keyword">type</span> 不能继续落子结果 = 渲染井字棋盘&lt;不能继续落子&gt;;</span><br></pre></td></tr></table></figure><table><thead><tr><th style="text-align:center">初始棋盘</th><th style="text-align:center">黑棋第一步 (一, 一)</th><th style="text-align:center">白棋第一步 (三, 一)</th></tr></thead><tbody><tr><td style="text-align:center"><img src="http://home.cs-tao.cc/github-content/contents/blog/image/fir/05-1.png" alt="初始棋盘.png"></td><td style="text-align:center"><img src="http://home.cs-tao.cc/github-content/contents/blog/image/fir/05-2.png" alt="黑棋第一步.png"></td><td style="text-align:center"><img src="http://home.cs-tao.cc/github-content/contents/blog/image/fir/05-3.png" alt="白棋第一步.png"></td></tr><tr><td style="text-align:center">黑棋第二步 (二, 二)</td><td style="text-align:center">白棋第二步 (三, 二)</td><td style="text-align:center">黑棋第三步 (三, 三)</td></tr><tr><td style="text-align:center"><img src="http://home.cs-tao.cc/github-content/contents/blog/image/fir/05-4.png" alt="黑棋第二步.png"></td><td style="text-align:center"><img src="http://home.cs-tao.cc/github-content/contents/blog/image/fir/05-5.png" alt="白棋第二步.png"></td><td style="text-align:center"><img src="http://home.cs-tao.cc/github-content/contents/blog/image/fir/05-6.png" alt="黑棋第三步.png"></td></tr></tbody></table><h1 id="相关链接"><a href="#相关链接" class="headerlink" title="相关链接"></a>相关链接</h1><ul><li>源代码：<a href="https://github.com/CS-Tao/ts-fir" target="_blank" rel="noopener">GitHub</a></li><li>在线体验：<a href="https://www.typescriptlang.org/play?#code/PTAEDMCcHsFtQBYBckAcDOAuEBzAlkggK4BGAdAMZzADCAygLQAqAhtMEug+HpAFB9gAKiF9QQ0IC-1QAIegbx9A0epihwPgFMAHqmiQkoJAE9Uq0IFg5QPfKgX4DQAXlAA7VQDdVkANxrN23QaOhAXl42oER2ACaqPA4h7hpaOnqGxoD0ZoFIkESq0Z5xPsaAZCqB4CwANugZAsKi4qCALqaADqaAH26AzoqAIW6AWdqAknJKKjFe8b6Ab2mBAN5ioIn9gOgqmGPuAL7uHrHeCaCABUqAAHIAPEygGkiqoeg1tQB8w6OAskrrgFRytdNM7qBjk9O5cws92SuAxta1w6BXW7TOquZ5TUD5eYCT7LXx1QKDAA+oF+HyysOMgHnFLaABfNbrt1PtDsczrY8X89gcQkcNps8HZwC4ATdTqMAPzM26jabmCxopZ9YzrQK0-onTICnKgQAxciKtutxYtelLAJBycs20sVMMFoEA22rqlVa9E6wApcurdUbJStALaK6pNluVK0AwHLq60Or6+QDWiuqne6MaBALpy6s9isEIiUoEA8gqAeL1ACvWgGD4wAL8YB07y6So9xnqgElvTajQA03iyCUTqccADT56X4ynEup8UmgAtVwlUo79dkNysU5vEttPJ4cxKjJ6vbkdpvF1vt3Kj7ObbGbRunUuged5zsnUPaqUmWeLoststjrsTkmBWf5lnlp5rrlnaslwf9iGjxL8x2+QDkmoA4uVz18L96OOoryPfca1qOtAj3ADQF7UAOWnYcQOg2CB1Hb85y2RcTmXVd103Y0pUAeB0fwvccDyAisyLAiDbCg7sSxQpD6MnPs4OfVjpkfUBphvY8D0YhDuNAYiMIXFlsJXTC8LfDNQG-LMSL-KiSwo69O1AlTwPrZJoN3S8jzOAchPQxdl14-CrV8YiFN-Bt-2YstKL46jtI0o49NuMz1zY5JphE0yDJk-1ADAdQASOWuOpADm5QjAAYlD9ACTjaNNlqQARv3rVK3NAABtUZ6UZSBjgARmAvKmTqAAmPgAF12zqQrMtrViOQqhrwNYp8dIc9C6uXCqTiHdqOSyiretqQqavavtpmGsbRvKib2p5SwBqWixR15IKdVqQB1bUAOlTAB4FQBP7XOJ5+mmfpHlAdZpnWS7pWmaVLpVaYVUu3Vpl1S6TWmE1LutaZrUup1pidS7PWmT1LoDaYA3eaECJ+WoDuO2wAHlYAIZK9qO5cACJ+lxiz32MQAmO0AZfMdqxzLflJUYAGtVH0aBwFAHbkcy0qCp27bADJvQAmOQGjluf5zKGaZlm2aOganx2zLJcOrLhb5hbJqF7beYFybEN5aWhJ1jjTEsBYw0qCRAGmLQAFbUAWXlADt-NMtxWS3ACorQAFdUAcAtAmFZFZWRNVkX1ZEzWRW1kRdZFvWRWH039S3AFa-d3PdAb2k9AP29RToPQBD0Aw9ACPA02qVAABzQjXY92xbujnUS-j8ursLx2LYT2wRieJ3pmd93LtjjuLdruGq6lQAQ80AAgTLfdzYy+XWv6zLzLO7d9ta-nvv3cF0BW619vQDLy7Jp70B+912YVsNtbtaN+HLMzQAYFVtzZbeK0BbfK+tH5Xtenxfj-F6a0BZ0fllXGTtcZVWXC-IBICqpnAAGT-xzIA3GsdQHgJtuVIByDoGn31nrS+g8ViADPdQA3QlWwfjbN+Nsf7tnQrbSBKCYKwNAKPcebtRK0OAaAiS7DMH9QvnyK+xNQCAGflEhZDlxFw-HPaCC8KFUKfCJdhUDxGSPdow5hTdWG0kUZw1BGDOHrTwQ7XwgAoOVEbbWR0iNHUJ-Nw+hYpQBwPURPbRYDJJkL0dA3huD+H4N8IAaDkzE22UcvSxM9Ai2zkWxBRNsPHBNUQ4phY8NHuI4a4rRMSkGcK8atBuvhACYqYAe+jbKAHzlQAB2qZXQKkekOBgKADPowAowaZWCHTOw0AADudhAh+BoqABpmVulPk3qxQAX4p5OmAAAzKdMAAJEMMpsxxl71ACfRCQy+yjImVM0Asz5mLN1g06YDSllQgEbJQAi3mACcgnmgB35UCLjQAvBuABZd3GEpBGAF83K5tzbAPMAEf7LzfHGD8Dcu5gBU8n+UY4wgBhRWBd8+5gAmnf+QC0Aly7lnPBQjXwHy7lvMRRC0AgAcjIuYEFFyIPm5OMJbbYmVCX1k3jc+4mUUUckucC6YHybkDzxWbQAG26AB4LE6oBAChioATu1e6bBpSnPwl1jHrF5R3PlkrLp+NlfK-lyIpV8BOXi5hfLABDyn8FuowRWqqASK0BnKMXGB1by2yOxoLWv1cBAACvPPlQCZW8pQaMAA0q63lQDlWepqrSo1orQBMFNcK81owPXTCdUqlVoBvUWuvqASKIrrW2r9ZGr1TwXXSLdbjD1uak3ZtxoG0BPTN7GvDdKxN8bRiBumMmzV5LQCAE2-HlvLADftqsQNVKC28uXPmhyXaA2ys4QKmtEbcZmqqnWuV4b3UTvnY2+tKbBE8sADIRU6w1drrVu+VO71UJsPaAbdirW2nP9Dqrd+rd3TAdbUDdslb1ZvtWPbld7ajOtdVu5dW6S2+oLf+ot6xAPBofUu2dUbV1PBlWehtTxlVnpbVqy1Hbt29pQwO0dX7h1-vHRBkNTxp05rg6ABD9wAPRuQ+BuNL7-TpuFW+0Ydq8Ogbnb+kDNGwE+sI+W8DlaSNCrDQ8GN9HQBIdACh5tjGdQ8qg9uy6gBQ-UALwhhLpiEsuoAZb9AA55tMAIyJtOjEAP7ygAKV0ADPKgBEFUAH8pgALCM4inN4V6kU6vvYa0je6v3yeHp+99Dl3M-tGCOk8hLgLAYckZglFzgIAFlMrJGRNOETZGYO0dAOpzTUndMGaTZdCzNmHPTDiwPCoEZAAU6g5wAtHKAHbgwAa8qAHBjVoW7rnNHA-bDDgBOC0AHbGDmGs8ua1u2yW6HN-slcBVrK9l7IjbPWUb9m-3tk6ieWcrXxGERnuvBbp9ev9fq4NlrmwFtEfofOVrG4DE+LxQN7lQ3bI4kUwWybtzQlr3rHt+zt37uPe5eR5cF2229Z5SK77R3Ri-ZFa6-7owptvcXh9vrX2Dt3aO5D4VAPrmhnK1UKr9m6tNdaIAdCU2s3EAKrKkLeWddTZ927OIt3NGaxTm1owKdjcHRN0YJOV5z1mz0tni3B3LcyrOEnG2y5eKfAL3bSO6cM6Z5TzYAvx1BpwlsEnl2+Ftoa-TxnrRmcPZ3SB4C3PLFlx6bTlHuuFc2vp-90AGuFg44kA15nIrqeCJ19cCnIrNg4gp36+s-vIXkad+GKodPbbNEth72SkebbNApw9p7eHgI4giZY97gQwfDd+8udPGSeEcwZEyen7Z6dLb-jr+X+vFf0-zy4yX3irtA56yD4V8fE+QuT9yqHBaYdPAL1QxH7ec9+-bw3wvk7oKc1AGXp8FeQPr2r3rg39e5+N+wctLX16dSAH+zQARsY8vpz311L2f6xNGIABtMjcceAhIkJo7V5u2AoAW-jADHcoAQmtnKaUCDXLPtgH+3+xe+UoAgAhBaAAxWYAHAqzQH+bUbEkBMBcBF+mSKsUSX4NkQBP6oAD+8S0EXEVeKOP2PKy4iBsB7+pwIBTIgAyvoXBbq6xPi0G34njboMF9gcizhYGY53gOQEFaxPisb8H8FDCibTA37kbLLARCHtRMG8ZSHSF9iyGCYQZsGTRN78GPqfrDaqFawiE1pKFzqSE6GTRKEIYloKF9iH7H7aEWGTR57GFazrYOGTQ37yG2E4Efi1xuG2G0hkFwHqEKEBH8FBFTRnxsGrS6wREGw4I5K75ShWFfo4gs6D7MEHjbom6vZP7ux0J8aD6WwrzioXLYSjASJSJP5TyjBYGtR-6lwAGgBVEz4l4FR+EUHtgtE-5HAsI5HrzWQ-hcEeES6Ja6wcgr425+5bqkHQHkGUGNGgG0FU78EcjzF+o6EcE5j9Ea5DEWEcjMaZqPYWzLjzHLqq6gBHGCZBohEGy7GfrJHuGnEXD+rpa5F3FnHFrPHuEJFbpJG2R3Fz4THOHtRi4AmsT7HeEWGlHuxgkKG+FTH+HAmgCXHtSIlRFawonRHb6hExFnxtrkx+C3GP4sHP7ARlGEkVFPCQoJyZ4I6BBOKsJTyHx4EOQUkewcjQq3KGYcpxErDky3FJF+rAQElpFEmjAklClkmgDMnD6BBJESGzFMgqbkwx5PgKlkz5GWKFFN4cgqlqlP6bCXIBEciUr6lAI3KVqTRiofInAmnXJmmhHakWz9Lry4k2r0leESmqJb7nzeJtqAA-RoAKXGBulR7++mFSVSdgNSowtBIZ0ElSkA1SPSH+0ZDkuMuMbEUZemQk4ysyiZemsw2yQw6ZCyOJZMW6vuEO-KHO6qwEkpVJAplJT+XhQZSZJ4sZ1SdyuMb+7+mRJ48ItgJc5uQeFZDks+8x7YH+3ZB4s+5uf8yxQuqsO89ZpJn8WsHI5MpZwqPxQhZxFa7xQhzJUJrEjZFh-pBuOZy4PJmw8x08kJC5L+7pbsG4B5fYtI45iJIR0wOZkR9xQ5J4Ayg09RwZemX5OCfCLeXJvgtWgA6foSI2Hnq97CrQ5PGdkTlgSew9Lt6nZVSV5sRQUwWbAYXKH0Ivldma5CTjna7QUfhE62Tbp954bkYD4AUoW-4VzoVfoioq6gIrFPi4VUX4XsXCqcVpJbCvnZJMULBSi1aAA7AYAJ2m1mdyTsgGKcSC1mqZyIuM3wgAOaZqWgC4yACzytpRJSsL1nJQ1sqhPJGRcIuUKZCaMHJZlNJXJT0vZdBMAoBu2LSZsLSLQdkakhJD5W7B4mJS5cmbHKpR5UkhPAFTkcuN5VZYFagaRdMCFSeBpdpRFSwl5VsNFX5bFdlfFUFcFfJa5QZbjBlckvODlVAhJHFb5TwmJRtAIFKDiNZokLkFJYAJRKtkKVB4jl1mae7eiFXGEO1lxILCnOeRCqlZ-gaehK1KsWownVgAAkaAAA6cpEcGnCaJ2cAdBL8GhUHl2txWxDiKNSWCwuvL9o8YYeqTiDSsMWMCLusV-tgctWtayIsWMOEaACZdZmZesBPCdXeXJTwSeLPuZb-CuXPq1e1V1Q4XJU+ZNSKgjTJv9R7FSWxODUJH4MjZjVSQCRyMDvBZ3knujsuODYiQbNjQ4bdQtRYa9Z5A4cRd-m+V9SBaEYJIhIJK8EZb4G1Z1Q9j1cSH1QNfBUNbBmnqdZ0RoiLT+WkXyrNUStBOFotR1atetanBnD0jiILSWH1eXtDfzdrf1XPhPnPjeZdfnuFqAPTe9d6U1SsFBTbePqLf3k8fWLxdRRDgRXOhBHKQVBIkTu2AHdTPAU+MHdBAGPdSaF+eHQ5CqPdfHeaaAIUpsOMoAEFmJggA9KaACAxoADAMsy5MlMAdJwswgAsAxE7R7crjJN4omxF8BSiACF0cmMzvpj3nRSwX9khSNWjVkS-l7VNXhkOhDnNUrRcj0o7arW9c7SKiDZOU0dbZPVyE+DbZlJtevHzV1cLSbfBfnubV2pbbFgvWrbbaBTvvXQ7ZBcqtcBZU8AFT-MBLQResbpZYdULvWI-YWjuUdRyJ5bVQldVYcQVYlaOB-aBihlxUvgIZFawlVURflXVVkqOBqufb4E3a3f3e3XLYxXfejbYJ5f2Tef-g+Rbm3kTUQeDojRjpZQg5Bn7fcawYwRcE-RxuvJbtYavormwaA4xSYUA1ArrLPcSCOUw3yvdR-bLWNaIx9eI7KbqbPjTYiRyDTfNQ4RyE3S3XppuRYaTfCQFcjaxOI-o32DTQCYIyWMIxo-jQ9dBLQZY78Uo4SvCdMFBVfTfb8YofFUY7w+kfCQYyIwse42Y0cGDTcMue4RyK4z3YSWE3cWo8mK3aTSjdfcQ+483r8WiQoRkxoWEdIVk6EeI46R9X+UnWzafZiRiXXXioAHnagAvaZqYWyn7Pbd333D2K0OTK1PAwXTa2WdNUU851HE3d555m0PlUEFSjmMMSNnVSPsHfmyO-n3Vj7DOA1BVjP3H0HSN0HYUfUJEn4An2HuErOoFGPXHbpaMKGfHfFMG73-30KUoKPKJE7FG-GmFCa7kWFKHgNoHSHPO2EwUOFBOgDyPt6qPb2YOSPkZWM6rnMWFrK-E1ro5LLuGzBePGM8pAJZYXIQPJlopsS4w4pCS4xoqouTVAKFZ2b2bYsnjJBxMJPt5nCGYkvb1ktWYUu2nuEU3lNem5M5NCF5NlMGwp3jK0G2z51DCF3bR+6+XVUov5kSswvGM0NsEl2l2ABPuoAJDmlsYrZx11upd10hSxDxDFuMpp3zi0uluMZd1dnpYFSK9TjTqerT81xJ7sGsosjMzMKISMOMowtcbr0EYsnrvwyMPSnlowwbR0WUZcGszxEbCsfr-MtDw589gN5ekt56Vif8v2QC+mVLB4xTszyj0E2buMmL7L-5vFue6Lbtaz-zBrHhqRY1z+oLHtztxrc6gLs+AdVjXT8O8JHIsdS5EN9jEImU6ERzmCyiW6nLH1+Q0E470r9Cxdvj-5tT9ryz5tVtMFjzM7fLydRS4yZcYr0b-MZdgAobHDISL7RivyvF1Wu7tJ1Cu1xisJt8znuXsfjXsF0UySswUqvWupP8t7ulPAcYk8sgfoncvmtCuAApeoAKfRgAjK5isltltVSzAMCzIocaZYtYXQSXKgssrXIOFsqfJl31MAfQcHsmAWD1OAAw-8h9W7m2hxh0MCW0x4ymPfW4RzoSRzcmXaq9JRR2U5UxhoALhKgA05pdoeYbyhqGZlbh4SAAACYQqAkAqgFALA+wIQoAwqgAyGaAB98TFIAEgJgASvrf6AAKaYAGxKse-opno8vKDTEmUznRCqVZJRfT0EtY9YpzfKmwEnUntQ9uHqy4fgnb89EzaZDxR1T4nafK2GsqCrV5bBwo0EwdHIAQ0wdnI8Dnl5DxaumwxdARNdvLddzuoAynqgqn6nmnqg2nenhnpnFn1n4g3QGGWXzWZzEmjb0zx6M17n3XgEwWvTROrUPSPnw2WXOX2NHhTzy4CGIXYXcxdB7YTB2zbEsXW68XsFvD-x-BKXDkdbQh6XPHoA7XrQZz1zbif7O7AjNrZ9ZXFXVXGnWnOnBnxnZntQVnNnOoo8HX3KBXH4W6I3nnlBeD-mowayNaZ3Zz03MFROy4hol0KLowRLHZowVNTws4cPCP-UoYKDxgJoFsZskp1gowyInlKoOPZPiSmVlPOcXi5P0DmwW1oA9o1PnlToVPTwjPmVnP9PPNVqI8hPZs260nOUTwT6wEkvow0vEvNxjqMv8vQ3NPeqyvsvKvvKCvcvAX1UAviSwvnXTw7GJ4o8BvX6WvUmAmXzPSxvB44vQL89BYznGvFvs+a4zv6vbvKoHvSvJUjvuoPvAXfvoBeYJogfqvwfTIeY1o4fmvyvbvTosfrvjvnoSf8fjvAYaf1UGVX65zcLfYNamaOhTvT5GPQhRfFh7vyNZf0hFftheY3vRjNfChdf7heYAfqLzfFhrfdxofQ9qTXfthPfvx0f-fqTfX4-GvCrbfifTLrEg-vxmaeYqfIXy4S-mfq--gpFqTD7fYu-u7nLLNwRSLRhk0g-TqA0DVeCfmZvYvivZv36UvQvRPovavz-Iv5vb-D-FvpvL-n-T-3-L-n-0f739gBP-d-q-11721fAv-D-rhhN4QDuU+qesLb2JD283e4GTKLANf6R8Coa4AbvrzAHp8Q+KoAgdgP-65R-eZAxASAKeBu8TQ1AwAbgIbDWhGBRA5gXmCdBsCP+tAh3iH09DcCcBlAkPgGEEGf9s+UDHlHnzYKF8aB0-OyLt3L5yCHC+A5GuQO24KEG+ig7vsoJH66htBQ-XQe41D4GD3C6g+QdIWj6mC7i5gldqxE4HWDF+cg5fhMUIE8CFwoghbrv1YjeCES8JA-gCx0IhFFQk0ZHpNFR7eFB+HNPwWfTxTkxhe-3X7K6hExZQxYDvAaOpXWBlVWI6laUNkL7DqUVQ+Q7nrpV1DFDlKJocoepWtBVDdKToWobjE9ANCAwoCaYGkLoYbNZmBAotOUKfCplAAvcSDChhwwkYaMOGG6Uvy3QvxL0LxagBAAXcQLDFhSw5YSsKWETCk6BAwNizDjaLNyGVbblMuDjZZQt00CNZvPkhqL4WG9bcZIACriOViWV9x24kKrPInvuS9bIxjh0CPMjcPGQncIOaTc1mzViInIpQ-nT-oEHt5gi4+wEKERb1hHK94RMIyThH1GCIjURyI6EeiKD5YjVeUA-HqADBF38ngYI3gSSLhHIjSRFI8kUIOJFUiERdIpETSIJF0i8ReKCTgkJpKIC-OyI5AcbD8yABUuW2juCQsAmMws8Uiwd0wGbzUNjcW5FB8ZqW-NZk73bBO9oubETNIiLshDpFRdDd3vdXd5qi-4GojEY6g7DajQuSo73qsQbDe9DRkNfPtIRrSxce0faBLiuxL4rtnR8XG1FX0wxxdXRPoxvlv334n9pCVGHLPCVkz5YASKyTJry2yb-C8mGTWutiWgGC9BRimMHhmJh4LclRHWJ8AWAIEMNZmo8bMbn0LG5jdR0oToX-HwFrcjRI8MscNlUE6jk2JAmsZDS0H1jIa0LeEg6IUJOisMrojQSP3Axz8-Rm3YcQuGrHLgNuW3BcKQK8ErsQhdxMIXcQiHwkF+QhPgj82I7xjUS+4yDsBTA4Ai66UodTA6W+QXj-kklKSncmkq4oMM5MDMYkJTwd0UhaQ+kBkN0pZDvxuMPIX+KKHfiAAFGxz0x5tiQAQLUvUyEjSUAAlFVDaGMw1mHYjkAQIvH3UsyrHatqh3Q6YccJ2HCCSWHw5cdPkJ3dlNcgWSTCHKUlTCfhM7pMc8J2EhieBNw4OQmUyKT5EJAolUSNh7rcWO8KljzklmJBQSQrBOGLdS8HYrNkWK-TWjbh9w9cuMXty8hDh3rcSV8NAA-C-hJ4o8SUxPHAi20EnF8ZyJfFO4wAwvfEcLwk4hlbA8Qonv93ZEOTQwUoYXmcgLCABTa0CD2snJokiIbpTyF4wihioVyUT3cnrAPJtk0APZKkFuTPJLklYMLzeSeTvJDTOKRFLxg4ogpHZXSsFPcChSzYyUiKVFJin-ckp8UvXnFOlBeTbA9rcqRlN0rEtcpOU3GGUJCmJSwpa4SKRmTslkwEhzPTqdVOxxgAMx+ImyT1OilkwXx3Il8QlN8DhSapoAHyeJxfF4wmpuMLIXjCyHtT5pnkkqVNKFH-cFpc04wEVMWn2sFpmUlqUUK2mEx8pKwM6ftOmlnSTpyKLqalM2BnS1pLUwKQFLun4j3J1Up6YdL1JdTXpyU6qR9MBkeSrp2UvGHkJ2mnSupwMqQRDI8mvT3JKoc6Q0zRnfS4ZzUxGW9KxkoyjpDfdGe4BAA2AzgIwSmU8Gui6U7hTyUACClAD3IfkWk-5LTJlDTBcYdwlmUzLZkcyKZYAJ4M9AZnMyJZTMm4ZzJFmgBpKPMs5AwCeQyzlkaY0AIAFnEwALCaME2qTjM8l4zmpt0xUJTMU6cAGAMQdTkgHNmQAYABULWfU2mAiENkulLZCKxti506e6wNVpqwti502ZpdF5KrKlD2yLYJMzYCHNenUdAA536ABbvx1lLSGmmMmGbpSyl-TspxssAKbK4AWyKAVslwLbNMAWBY5DsjeKAGdm4wtk1HOjrnUVlPI1WD40ECfG3BFy45ociac+JBnRzW54oIAA" target="_blank" rel="noopener">TypeScript Playground</a></li><li>参考项目：<a href="https://github.com/chinese-chess-everywhere/type-chess" target="_blank" rel="noopener">用 TypeScript 类型系统写的中国象棋</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;http://home.cs-tao.cc/github-content/contents/blog/image/fir/00.jpeg&quot; width=&quot;40%&quot; height=&quot;40%&quot;&gt;&lt;/p&gt;
&lt;p&gt;TypeScript 是一个类型系统，如何实现五子棋应用呢？这不得不提到 TypeScript 的类型系统其实是图灵完备的 🤓&lt;/p&gt;
    
    </summary>
    
      <category term="技术" scheme="https://cs-tao.github.io/blog/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
      <category term="typescript" scheme="https://cs-tao.github.io/blog/tags/typescript/"/>
    
  </entry>
  
  <entry>
    <title>使用 git bisect 二分法快速定位问题</title>
    <link href="https://cs-tao.github.io/blog/%E6%8A%80%E6%9C%AF/git-bisect-%E4%BA%8C%E5%88%86%E6%B3%95%E5%BF%AB%E9%80%9F%E5%AE%9A%E4%BD%8D%E9%97%AE%E9%A2%98/"/>
    <id>https://cs-tao.github.io/blog/技术/git-bisect-二分法快速定位问题/</id>
    <published>2024-09-10T03:55:20.000Z</published>
    <updated>2025-01-28T04:15:07.206Z</updated>
    
    <content type="html"><![CDATA[<p><img src="http://home.cs-tao.cc/github-content/contents/blog/image/git-bisect.jpeg?1" width="36%" height="36%"></p><p>最近作为「内包」支援公司内的另一个项目，需要和另一个团队的同事们合作，不熟悉他们的项目，在经过两周的合作后，某个不常用的页面终于打不开了。</p><p>这个时候我们便可以使用 git 自带的二分查找工具 <code>git bisect</code> 来快速定位出问题的提交，找相关人员排查问题。</p><a id="more"></a><p><code>git bisect</code> 最常用的命令有如下三个：</p><figure class="highlight bash"><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 开始(重置)二分查找流程</span></span><br><span class="line">❯ git bisect start</span><br><span class="line"></span><br><span class="line"><span class="comment"># 标记当前(或指定)提交为有问题的提交</span></span><br><span class="line">❯ git bisect bad [&lt;rev&gt;]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 标记当前(或指定)提交为正常的提交</span></span><br><span class="line">❯ git bisect good [&lt;rev&gt;]</span><br></pre></td></tr></table></figure><p>下面先记录我是如何用这三个命令快速定位问题的，然后会介绍一下 bisect 其它命令和其使用场景。</p><h2 id="问题定位"><a href="#问题定位" class="headerlink" title="问题定位"></a>问题定位</h2><ol><li><p>首先使用你熟悉的 git 工具查看一下提交历史，我使用的是 <a href="https://github.com/jonas/tig" target="_blank" rel="noopener">tig</a>，提交历史如下</p><figure class="highlight bash"><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></pre></td><td class="code"><pre><span class="line">o [HEAD] [eacd4a0a85bfcc8691f22368079acab7412575d7] feat: ********</span><br><span class="line">M─┐ Merge branch <span class="string">'********'</span> into <span class="string">'********'</span></span><br><span class="line">│ o [66504f3e0b73b568d379adbaf0e7b031d595187e] feat: ********</span><br><span class="line">o─┘ [58625437bda334df576eb5b5031fba7ec990be65] fix: ********</span><br><span class="line">o [07ee37373716bfb75f391ab3b65f68640a5e3951] feat: ********</span><br><span class="line">o [ee948eb50bfcf7bfe47790f50053439f05ac0e5a] feat: ********</span><br><span class="line">o [fedd7e523f90679ae0048138f5cf21ebf07be733] fix: ********</span><br><span class="line">o [8ad08f09eb3a3225af9c05e59210c99ecd53f330] fix: ********</span><br><span class="line">o [f71b6535d88613cb08211a277ce7f2c92c83d00e] feat: ********</span><br><span class="line">o [e160869ec18a1980e100acc07d81a385392a8039] feat: ********</span><br><span class="line">o [79a20e29636178e33479ec35cd23101b4080a6aa] feat: ********</span><br><span class="line">o [fbdc27d4498e4d27828e8b7037d68c73f272878d] feat: ********</span><br><span class="line">o [83900f6d009f0158dfce0fccc241b2fa013d1743] fix: ********</span><br><span class="line">o [782c1fa6852522d5db16cfff88c7051c2ac0bf09] feat: ********</span><br><span class="line">o [f3517468c23495063f99a9e49c657745d95ba88f] chore: ********</span><br><span class="line">o [f11d08852b7fb0ef7e9d16056280edac5a772d57] feat: ********</span><br><span class="line">o [839367048641d147cd28a38991a5c6d7dca3bb0d] feat: ********</span><br><span class="line">o [147833f0a4f4ed7de21b62d919e937c4c6ec6cc4] feat: ********</span><br><span class="line">o [43835476244b330ab8bd119e94c45aeaf0f32afa] chore: ********</span><br><span class="line">o [e0c12e65ec86db44557756ddfc2ad510878ea9bf] feat: ********</span><br><span class="line">M─┐ [ee62949cc48a5f60b16ef5fa62d0418ee36ac855] Merge branch <span class="string">'********'</span> into <span class="string">'release-24-09-02'</span></span><br><span class="line">│ o [5cd65c0fd7ee8309d6ddec38e232eeae19f93a03] chore: 发布正式版本</span><br></pre></td></tr></table></figure></li><li><p>接下来执行 <code>git bisect start</code> 命令，进入二分查找模式，并标记最新的提交为有问题的提交</p><figure class="highlight bash"><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">❯ git bisect start</span><br><span class="line">status: waiting <span class="keyword">for</span> both good and bad commits</span><br><span class="line"></span><br><span class="line">❯ git bisect bad</span><br><span class="line">status: waiting <span class="keyword">for</span> good commit(s), bad commit known</span><br></pre></td></tr></table></figure></li><li><p>根据提示，接下来我们需要标记正常的提交。我们审视一下上面的提交历史，可以发现 <code>[ee62949cc48a5f60b16ef5fa62d0418ee36ac855] Merge branch &#39;********&#39; into &#39;release-24-09-02&#39;</code> 看起来是上次发布上线的版本，我们切到这个提交上测试一下</p><figure class="highlight bash"><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">❯ git checkout ee62949cc48a5f60b16ef5fa62d0418ee36ac855</span><br><span class="line">Note: switching to <span class="string">'ee62949cc48a5f60b16ef5fa62d0418ee36ac855'</span></span><br></pre></td></tr></table></figure><p>发现页面能正常打开，所以我们可以标记此提交是正常的</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">❯ git bisect good</span><br><span class="line">Bisecting: 10 revisions left to <span class="built_in">test</span> after this (roughly 3 steps)</span><br><span class="line">[79a20e29636178e33479ec35cd23101b4080a6aa] feat: ********</span><br></pre></td></tr></table></figure><p>此时的提交历史检视结果如下（同时可以关注到 git 已经自动切换到了 <code>79a20e29636178e33479ec35cd23101b4080a6aa</code> 提交）</p><figure class="highlight bash"><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></pre></td><td class="code"><pre><span class="line">❌ o [HEAD] [eacd4a0a85bfcc8691f22368079acab7412575d7] feat: ********</span><br><span class="line">❔ M─┐ Merge branch <span class="string">'********'</span> into <span class="string">'********'</span></span><br><span class="line">❔ │ o [66504f3e0b73b568d379adbaf0e7b031d595187e] feat: ********</span><br><span class="line">❔ o─┘ [58625437bda334df576eb5b5031fba7ec990be65] fix: ********</span><br><span class="line">❔ o [07ee37373716bfb75f391ab3b65f68640a5e3951] feat: ********</span><br><span class="line">❔ o [ee948eb50bfcf7bfe47790f50053439f05ac0e5a] feat: ********</span><br><span class="line">❔ o [fedd7e523f90679ae0048138f5cf21ebf07be733] fix: ********nv</span><br><span class="line">❔ o [8ad08f09eb3a3225af9c05e59210c99ecd53f330] fix: ********</span><br><span class="line">❔ o [f71b6535d88613cb08211a277ce7f2c92c83d00e] feat: ********</span><br><span class="line">❔ o [e160869ec18a1980e100acc07d81a385392a8039] feat: ********</span><br><span class="line">❔ o [79a20e29636178e33479ec35cd23101b4080a6aa] feat: ********</span><br><span class="line">❔ o [fbdc27d4498e4d27828e8b7037d68c73f272878d] feat: ********</span><br><span class="line">❔ o [83900f6d009f0158dfce0fccc241b2fa013d1743] fix: ********</span><br><span class="line">❔ o [782c1fa6852522d5db16cfff88c7051c2ac0bf09] feat: ********</span><br><span class="line">❔ o [f3517468c23495063f99a9e49c657745d95ba88f] chore: ********</span><br><span class="line">❔ o [f11d08852b7fb0ef7e9d16056280edac5a772d57] feat: ********</span><br><span class="line">❔ o [839367048641d147cd28a38991a5c6d7dca3bb0d] feat: ********</span><br><span class="line">❔ o [147833f0a4f4ed7de21b62d919e937c4c6ec6cc4] feat: ********</span><br><span class="line">❔ o [43835476244b330ab8bd119e94c45aeaf0f32afa] chore: ********</span><br><span class="line">❔ o [e0c12e65ec86db44557756ddfc2ad510878ea9bf] feat: ********</span><br><span class="line">✅ M─┐ [ee62949cc48a5f60b16ef5fa62d0418ee36ac855] Merge branch <span class="string">'********'</span> into <span class="string">'release-24-09-02'</span></span><br><span class="line">│ o [5cd65c0fd7ee8309d6ddec38e232eeae19f93a03] chore: 发布正式版本</span><br></pre></td></tr></table></figure><p>继续检查当前提交是否正常，如果正常，则执行 <code>git bisect good</code>，如果不正常，则执行 <code>git bisect bad</code>，直至找到首个有问题的提交。</p><blockquote><p>每次检查当前提交是否正常之前需要执行一些准备命令确保外部依赖被正确安装。比如前端项目中一般需要执行 <code>npm install</code> 命令来更新依赖</p></blockquote></li><li><p>对当前提交 <code>79a20e29636178e33479ec35cd23101b4080a6aa</code> 进行测试，标记当前提交是有问题的</p><figure class="highlight bash"><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></pre></td><td class="code"><pre><span class="line">❯ git bisect bad</span><br><span class="line">Bisecting: 4 revisions left to <span class="built_in">test</span> after this (roughly 2 steps)</span><br><span class="line">[f11d08852b7fb0ef7e9d16056280edac5a772d57] feat: ********</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查找结果</span></span><br><span class="line">❌ o [HEAD] [eacd4a0a85bfcc8691f22368079acab7412575d7] feat: ********</span><br><span class="line">❌ M─┐ Merge branch <span class="string">'********'</span> into <span class="string">'********'</span></span><br><span class="line">❌ │ o [66504f3e0b73b568d379adbaf0e7b031d595187e] feat: ********</span><br><span class="line">❌ o─┘ [58625437bda334df576eb5b5031fba7ec990be65] fix: ********</span><br><span class="line">❌ o [07ee37373716bfb75f391ab3b65f68640a5e3951] feat: ********</span><br><span class="line">❌ o [ee948eb50bfcf7bfe47790f50053439f05ac0e5a] feat: ********</span><br><span class="line">❌ o [fedd7e523f90679ae0048138f5cf21ebf07be733] fix: ********</span><br><span class="line">❌ o [8ad08f09eb3a3225af9c05e59210c99ecd53f330] fix: ********</span><br><span class="line">❌ o [f71b6535d88613cb08211a277ce7f2c92c83d00e] feat: ********</span><br><span class="line">❌ o [e160869ec18a1980e100acc07d81a385392a8039] feat: ********</span><br><span class="line">❌ o [79a20e29636178e33479ec35cd23101b4080a6aa] feat: ********</span><br><span class="line">❔ o [fbdc27d4498e4d27828e8b7037d68c73f272878d] feat: ********</span><br><span class="line">❔ o [83900f6d009f0158dfce0fccc241b2fa013d1743] fix: ********</span><br><span class="line">❔ o [782c1fa6852522d5db16cfff88c7051c2ac0bf09] feat: ********</span><br><span class="line">❔ o [f3517468c23495063f99a9e49c657745d95ba88f] chore: ********</span><br><span class="line">❔ o [f11d08852b7fb0ef7e9d16056280edac5a772d57] feat: ********</span><br><span class="line">❔ o [839367048641d147cd28a38991a5c6d7dca3bb0d] feat: ********</span><br><span class="line">❔ o [147833f0a4f4ed7de21b62d919e937c4c6ec6cc4] feat: ********</span><br><span class="line">❔ o [43835476244b330ab8bd119e94c45aeaf0f32afa] chore: ********</span><br><span class="line">❔ o [e0c12e65ec86db44557756ddfc2ad510878ea9bf] feat: ********</span><br><span class="line">✅ M─┐ [ee62949cc48a5f60b16ef5fa62d0418ee36ac855] Merge branch <span class="string">'********'</span> into <span class="string">'release-24-09-02'</span></span><br><span class="line">│ o [5cd65c0fd7ee8309d6ddec38e232eeae19f93a03] chore: 发布正式版本</span><br></pre></td></tr></table></figure><p>命令执行后，git 自动切换到了 <code>f11d08852b7fb0ef7e9d16056280edac5a772d57</code> 提交。</p></li><li><p>对当前提交 <code>f11d08852b7fb0ef7e9d16056280edac5a772d57</code> 进行测试，标记当前提交是正常的</p><figure class="highlight bash"><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></pre></td><td class="code"><pre><span class="line">❯ git bisect good</span><br><span class="line">Bisecting: 2 revisions left to <span class="built_in">test</span> after this (roughly 1 step)</span><br><span class="line">[782c1fa6852522d5db16cfff88c7051c2ac0bf09] feat: ********</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查找结果</span></span><br><span class="line">❌ o [HEAD] [eacd4a0a85bfcc8691f22368079acab7412575d7] feat: ********</span><br><span class="line">❌ M─┐ Merge branch <span class="string">'********'</span> into <span class="string">'********'</span></span><br><span class="line">❌ │ o [66504f3e0b73b568d379adbaf0e7b031d595187e] feat: ********</span><br><span class="line">❌ o─┘ [58625437bda334df576eb5b5031fba7ec990be65] fix: ********</span><br><span class="line">❌ o [07ee37373716bfb75f391ab3b65f68640a5e3951] feat: ********</span><br><span class="line">❌ o [ee948eb50bfcf7bfe47790f50053439f05ac0e5a] feat: ********</span><br><span class="line">❌ o [fedd7e523f90679ae0048138f5cf21ebf07be733] fix: ********</span><br><span class="line">❌ o [8ad08f09eb3a3225af9c05e59210c99ecd53f330] fix: ********</span><br><span class="line">❌ o [f71b6535d88613cb08211a277ce7f2c92c83d00e] feat: ********</span><br><span class="line">❌ o [e160869ec18a1980e100acc07d81a385392a8039] feat: ********</span><br><span class="line">❌ o [79a20e29636178e33479ec35cd23101b4080a6aa] feat: ********</span><br><span class="line">❔ o [fbdc27d4498e4d27828e8b7037d68c73f272878d] feat: ********</span><br><span class="line">❔ o [83900f6d009f0158dfce0fccc241b2fa013d1743] fix: ********</span><br><span class="line">❔ o [782c1fa6852522d5db16cfff88c7051c2ac0bf09] feat: ********</span><br><span class="line">❔ o [f3517468c23495063f99a9e49c657745d95ba88f] chore: ********</span><br><span class="line">✅ o [f11d08852b7fb0ef7e9d16056280edac5a772d57] feat: ********</span><br><span class="line">✅ o [839367048641d147cd28a38991a5c6d7dca3bb0d] feat: ********</span><br><span class="line">✅ o [147833f0a4f4ed7de21b62d919e937c4c6ec6cc4] feat: ********</span><br><span class="line">✅ o [43835476244b330ab8bd119e94c45aeaf0f32afa] chore: ********</span><br><span class="line">✅ o [e0c12e65ec86db44557756ddfc2ad510878ea9bf] feat: ********</span><br><span class="line">✅ M─┐ [ee62949cc48a5f60b16ef5fa62d0418ee36ac855] Merge branch <span class="string">'********'</span> into <span class="string">'release-24-09-02'</span></span><br><span class="line">│ o [5cd65c0fd7ee8309d6ddec38e232eeae19f93a03] chore: 发布正式版本</span><br></pre></td></tr></table></figure><p>命令执行后，git 自动切换到了 <code>782c1fa6852522d5db16cfff88c7051c2ac0bf09</code> 提交。</p></li><li><p>对当前提交 <code>782c1fa6852522d5db16cfff88c7051c2ac0bf09</code> 进行测试，标记当前提交是正常的</p><figure class="highlight bash"><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></pre></td><td class="code"><pre><span class="line">❯ git bisect good</span><br><span class="line">Bisecting: 0 revisions left to <span class="built_in">test</span> after this (roughly 1 step)</span><br><span class="line">[fbdc27d4498e4d27828e8b7037d68c73f272878d] feat: ********</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查找结果</span></span><br><span class="line">❌ o [HEAD] [eacd4a0a85bfcc8691f22368079acab7412575d7] feat: ********</span><br><span class="line">❌ M─┐ Merge branch <span class="string">'********'</span> into <span class="string">'********'</span></span><br><span class="line">❌ │ o [66504f3e0b73b568d379adbaf0e7b031d595187e] feat: ********</span><br><span class="line">❌ o─┘ [58625437bda334df576eb5b5031fba7ec990be65] fix: ********</span><br><span class="line">❌ o [07ee37373716bfb75f391ab3b65f68640a5e3951] feat: ********</span><br><span class="line">❌ o [ee948eb50bfcf7bfe47790f50053439f05ac0e5a] feat: ********</span><br><span class="line">❌ o [fedd7e523f90679ae0048138f5cf21ebf07be733] fix: ********</span><br><span class="line">❌ o [8ad08f09eb3a3225af9c05e59210c99ecd53f330] fix: ********</span><br><span class="line">❌ o [f71b6535d88613cb08211a277ce7f2c92c83d00e] feat: ********</span><br><span class="line">❌ o [e160869ec18a1980e100acc07d81a385392a8039] feat: ********</span><br><span class="line">❌ o [79a20e29636178e33479ec35cd23101b4080a6aa] feat: ********</span><br><span class="line">❔ o [fbdc27d4498e4d27828e8b7037d68c73f272878d] feat: ********</span><br><span class="line">❔ o [83900f6d009f0158dfce0fccc241b2fa013d1743] fix: ********</span><br><span class="line">✅ o [782c1fa6852522d5db16cfff88c7051c2ac0bf09] feat: ********</span><br><span class="line">✅ o [f3517468c23495063f99a9e49c657745d95ba88f] chore: ********</span><br><span class="line">✅ o [f11d08852b7fb0ef7e9d16056280edac5a772d57] feat: ********</span><br><span class="line">✅ o [839367048641d147cd28a38991a5c6d7dca3bb0d] feat: ********</span><br><span class="line">✅ o [147833f0a4f4ed7de21b62d919e937c4c6ec6cc4] feat: ********</span><br><span class="line">✅ o [43835476244b330ab8bd119e94c45aeaf0f32afa] chore: ********</span><br><span class="line">✅ o [e0c12e65ec86db44557756ddfc2ad510878ea9bf] feat: ********</span><br><span class="line">✅ M─┐ [ee62949cc48a5f60b16ef5fa62d0418ee36ac855] Merge branch <span class="string">'********'</span> into <span class="string">'release-24-09-02'</span></span><br><span class="line">│ o [5cd65c0fd7ee8309d6ddec38e232eeae19f93a03] chore: 发布正式版本</span><br></pre></td></tr></table></figure><p>命令执行后，git 自动切换到了 <code>fbdc27d4498e4d27828e8b7037d68c73f272878d</code> 提交。</p></li><li><p>对当前提交 <code>fbdc27d4498e4d27828e8b7037d68c73f272878d</code> 进行测试，标记当前提交是有问题的</p><figure class="highlight bash"><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></pre></td><td class="code"><pre><span class="line">❯ git bisect bad</span><br><span class="line">Bisecting: 0 revisions left to <span class="built_in">test</span> after this (roughly 0 steps)</span><br><span class="line">[83900f6d009f0158dfce0fccc241b2fa013d1743] fix: ********</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查找结果</span></span><br><span class="line">❌ o [HEAD] [eacd4a0a85bfcc8691f22368079acab7412575d7] feat: ********</span><br><span class="line">❌ M─┐ Merge branch <span class="string">'********'</span> into <span class="string">'********'</span></span><br><span class="line">❌ │ o [66504f3e0b73b568d379adbaf0e7b031d595187e] feat: ********</span><br><span class="line">❌ o─┘ [58625437bda334df576eb5b5031fba7ec990be65] fix: ********</span><br><span class="line">❌ o [07ee37373716bfb75f391ab3b65f68640a5e3951] feat: ********</span><br><span class="line">❌ o [ee948eb50bfcf7bfe47790f50053439f05ac0e5a] feat: ********</span><br><span class="line">❌ o [fedd7e523f90679ae0048138f5cf21ebf07be733] fix: ********</span><br><span class="line">❌ o [8ad08f09eb3a3225af9c05e59210c99ecd53f330] fix: ********</span><br><span class="line">❌ o [f71b6535d88613cb08211a277ce7f2c92c83d00e] feat: ********</span><br><span class="line">❌ o [e160869ec18a1980e100acc07d81a385392a8039] feat: ********</span><br><span class="line">❌ o [79a20e29636178e33479ec35cd23101b4080a6aa] feat: ********</span><br><span class="line">❌ o [fbdc27d4498e4d27828e8b7037d68c73f272878d] feat: ********</span><br><span class="line">❔ o [83900f6d009f0158dfce0fccc241b2fa013d1743] fix: ********</span><br><span class="line">✅ o [782c1fa6852522d5db16cfff88c7051c2ac0bf09] feat: ********</span><br><span class="line">✅ o [f3517468c23495063f99a9e49c657745d95ba88f] chore: ********</span><br><span class="line">✅ o [f11d08852b7fb0ef7e9d16056280edac5a772d57] feat: ********</span><br><span class="line">✅ o [839367048641d147cd28a38991a5c6d7dca3bb0d] feat: ********</span><br><span class="line">✅ o [147833f0a4f4ed7de21b62d919e937c4c6ec6cc4] feat: ********</span><br><span class="line">✅ o [43835476244b330ab8bd119e94c45aeaf0f32afa] chore: ********</span><br><span class="line">✅ o [e0c12e65ec86db44557756ddfc2ad510878ea9bf] feat: ********</span><br><span class="line">✅ M─┐ [ee62949cc48a5f60b16ef5fa62d0418ee36ac855] Merge branch <span class="string">'********'</span> into <span class="string">'release-24-09-02'</span></span><br><span class="line">│ o [5cd65c0fd7ee8309d6ddec38e232eeae19f93a03] chore: 发布正式版本</span><br></pre></td></tr></table></figure><p>命令执行后，git 自动切换到了 <code>83900f6d009f0158dfce0fccc241b2fa013d1743</code> 提交。</p></li><li><p>对当前提交 <code>83900f6d009f0158dfce0fccc241b2fa013d1743</code> 进行测试，标记当前提交是正常的</p><figure class="highlight bash"><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></pre></td><td class="code"><pre><span class="line">❯ git bisect good</span><br><span class="line">fbdc27d4498e4d27828e8b7037d68c73f272878d is the first bad commit</span><br><span class="line">commit fbdc27d4498e4d27828e8b7037d68c73f272878d</span><br><span class="line">Author: ******** &lt;********@********.com&gt;</span><br><span class="line">Date:   Thu Sep 5 15:11:00 2024 +0800</span><br><span class="line"></span><br><span class="line">    feat: ********</span><br><span class="line"></span><br><span class="line">.../********.tsx                           |   5 +</span><br><span class="line">src/********.js                            |   4 +</span><br><span class="line">src/********.d.ts                          |   5 +</span><br><span class="line">src/********.ts                            |  10 ++</span><br><span class="line">.../********.ts                            |   2 +</span><br><span class="line">src/********.ts                            |  21 ++++</span><br><span class="line">src/********.ts                            |  36 ++++--</span><br><span class="line">.../********.test.ts                       | 114 ++++++++++++++++++</span><br><span class="line">src/********.ts                            |  88 ++++++++++++++</span><br><span class="line">.../********.ts                            | 133 ++++++++++++++++++---</span><br><span class="line">src/********.ts                            |  47 ++++++--</span><br><span class="line">src/********.ts                            |   6 +</span><br><span class="line">src/********.ts                            |   2 +</span><br><span class="line">src/********.ts                            |  78 ++++++++++++</span><br><span class="line">src/********.tsx                           |   2 +</span><br><span class="line">src/********.tsx                           |   5 +</span><br><span class="line">16 files changed, 524 insertions(+), 34 deletions(-)</span><br><span class="line">create mode 100644 src/********.ts</span><br><span class="line">create mode 100644 src/********.ts</span><br><span class="line">create mode 100644 src/********.test.ts</span><br><span class="line">create mode 100644 src/********.ts</span><br><span class="line">create mode 100644 src/********.ts</span><br><span class="line">create mode 100644 src/********.ts</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查找结果</span></span><br><span class="line">❌ o [HEAD] [eacd4a0a85bfcc8691f22368079acab7412575d7] feat: ********</span><br><span class="line">❌ M─┐ Merge branch <span class="string">'********'</span> into <span class="string">'********'</span></span><br><span class="line">❌ │ o [66504f3e0b73b568d379adbaf0e7b031d595187e] feat: ********</span><br><span class="line">❌ o─┘ [58625437bda334df576eb5b5031fba7ec990be65] fix: ********</span><br><span class="line">❌ o [07ee37373716bfb75f391ab3b65f68640a5e3951] feat: ********</span><br><span class="line">❌ o [ee948eb50bfcf7bfe47790f50053439f05ac0e5a] feat: ********</span><br><span class="line">❌ o [fedd7e523f90679ae0048138f5cf21ebf07be733] fix: ********</span><br><span class="line">❌ o [8ad08f09eb3a3225af9c05e59210c99ecd53f330] fix: ********</span><br><span class="line">❌ o [f71b6535d88613cb08211a277ce7f2c92c83d00e] feat: ********</span><br><span class="line">❌ o [e160869ec18a1980e100acc07d81a385392a8039] feat: ********</span><br><span class="line">❌ o [79a20e29636178e33479ec35cd23101b4080a6aa] feat: ********</span><br><span class="line">❌ o [fbdc27d4498e4d27828e8b7037d68c73f272878d] feat: ******** &lt;-- 首个有问题的提交❗️❗️❗️</span><br><span class="line">✅ o [83900f6d009f0158dfce0fccc241b2fa013d1743] fix: ********</span><br><span class="line">✅ o [782c1fa6852522d5db16cfff88c7051c2ac0bf09] feat: ********</span><br><span class="line">✅ o [f3517468c23495063f99a9e49c657745d95ba88f] chore: ********</span><br><span class="line">✅ o [f11d08852b7fb0ef7e9d16056280edac5a772d57] feat: ********</span><br><span class="line">✅ o [839367048641d147cd28a38991a5c6d7dca3bb0d] feat: ********</span><br><span class="line">✅ o [147833f0a4f4ed7de21b62d919e937c4c6ec6cc4] feat: ********</span><br><span class="line">✅ o [43835476244b330ab8bd119e94c45aeaf0f32afa] chore: ********</span><br><span class="line">✅ o [e0c12e65ec86db44557756ddfc2ad510878ea9bf] feat: ********</span><br><span class="line">✅ M─┐ [ee62949cc48a5f60b16ef5fa62d0418ee36ac855] Merge branch <span class="string">'********'</span> into <span class="string">'release-24-09-02'</span></span><br><span class="line">│ o [5cd65c0fd7ee8309d6ddec38e232eeae19f93a03] chore: 发布正式版本</span><br></pre></td></tr></table></figure></li></ol><h2 id="其它命令"><a href="#其它命令" class="headerlink" title="其它命令"></a>其它命令</h2><p>你只需知道 <code>git bisect help</code> 命令就够了 💯</p><figure class="highlight bash"><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></pre></td><td class="code"><pre><span class="line">❯ git bisect <span class="built_in">help</span></span><br><span class="line">usage: git bisect [<span class="built_in">help</span>|start|bad|good|new|old|terms|skip|next|reset|visualize|view|replay|<span class="built_in">log</span>|run]</span><br><span class="line"></span><br><span class="line">git bisect <span class="built_in">help</span></span><br><span class="line">        <span class="built_in">print</span> this long <span class="built_in">help</span> message.</span><br><span class="line">git bisect start [--term-&#123;new,bad&#125;=&lt;term&gt; --term-&#123;old,good&#125;=&lt;term&gt;]</span><br><span class="line">                 [--no-checkout] [--first-parent] [&lt;bad&gt; [&lt;good&gt;...]] [--] [&lt;pathspec&gt;...]</span><br><span class="line">        reset bisect state and start bisection.</span><br><span class="line">git bisect (bad|new) [&lt;rev&gt;]</span><br><span class="line">        mark &lt;rev&gt; a known-bad revision/</span><br><span class="line">                a revision after change <span class="keyword">in</span> a given property.</span><br><span class="line">git bisect (good|old) [&lt;rev&gt;...]</span><br><span class="line">        mark &lt;rev&gt;... known-good revisions/</span><br><span class="line">                revisions before change <span class="keyword">in</span> a given property.</span><br><span class="line">git bisect terms [--term-good | --term-bad]</span><br><span class="line">        show the terms used <span class="keyword">for</span> old and new commits (default: bad, good)</span><br><span class="line">git bisect skip [(&lt;rev&gt;|&lt;range&gt;)...]</span><br><span class="line">        mark &lt;rev&gt;... untestable revisions.</span><br><span class="line">git bisect next</span><br><span class="line">        find next bisection to <span class="built_in">test</span> and check it out.</span><br><span class="line">git bisect reset [&lt;commit&gt;]</span><br><span class="line">        finish bisection search and go back to commit.</span><br><span class="line">git bisect (visualize|view)</span><br><span class="line">        show bisect status <span class="keyword">in</span> gitk.</span><br><span class="line">git bisect replay &lt;logfile&gt;</span><br><span class="line">        replay bisection <span class="built_in">log</span>.</span><br><span class="line">git bisect <span class="built_in">log</span></span><br><span class="line">        show bisect <span class="built_in">log</span>.</span><br><span class="line">git bisect run &lt;cmd&gt;...</span><br><span class="line">        use &lt;cmd&gt;... to automatically bisect.</span><br><span class="line"></span><br><span class="line">Please use <span class="string">"git help bisect"</span> to get the full man page.</span><br></pre></td></tr></table></figure><p>这些命令都比较简单，但必须重点提一下的是 <code>git bisect run &lt;cmd&gt;...</code> 命令，在标记了 good 和 bad 提交之后，可以通过此命令自动查找出问题的提交。</p><p>举一个前端常见的 🌰：<strong>依赖安装失败</strong>。前端项目免不了进行依赖安装，如果依赖安装失败后面的工作几乎无法开展。</p><p>当遇到最新的代码无法安装依赖，但历史版本可以安装时：我们可以标记最新的提交为 bad，标记某个历史提交为 good，然后执行 <code>git bisect run npm i</code>，接下来 git 便会<strong>自动</strong>以二分顺序切出各个提交，逐一执行 <code>npm i</code> 命令。如果命令以非 0 退出，则标记该提交为 bad，反之标记为 good，直至找到首次出问题的提交。</p><h2 id="思路扩展"><a href="#思路扩展" class="headerlink" title="思路扩展"></a>思路扩展</h2><p>同样的思路，在项目实践中，我们可以将主分支上的每一个提交都部署到测试环境，当出现一些不好定位的问题时，通过类似的工具去标记「正常的版本」和「有问题的版本」，结合人工检测或脚本检测（类似于 e2e 测试脚本）来自动查找首次出问题的版本。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;http://home.cs-tao.cc/github-content/contents/blog/image/git-bisect.jpeg?1&quot; width=&quot;36%&quot; height=&quot;36%&quot;&gt;&lt;/p&gt;
&lt;p&gt;最近作为「内包」支援公司内的另一个项目，需要和另一个团队的同事们合作，不熟悉他们的项目，在经过两周的合作后，某个不常用的页面终于打不开了。&lt;/p&gt;
&lt;p&gt;这个时候我们便可以使用 git 自带的二分查找工具 &lt;code&gt;git bisect&lt;/code&gt; 来快速定位出问题的提交，找相关人员排查问题。&lt;/p&gt;
    
    </summary>
    
      <category term="技术" scheme="https://cs-tao.github.io/blog/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
      <category term="git" scheme="https://cs-tao.github.io/blog/tags/git/"/>
    
  </entry>
  
  <entry>
    <title>重新出发</title>
    <link href="https://cs-tao.github.io/blog/%E7%94%9F%E6%B4%BB/%E9%87%8D%E6%96%B0%E5%87%BA%E5%8F%91/"/>
    <id>https://cs-tao.github.io/blog/生活/重新出发/</id>
    <published>2024-08-18T02:47:20.000Z</published>
    <updated>2025-01-28T04:15:07.206Z</updated>
    
    <content type="html"><![CDATA[<p>我打算把大学期间的技术博客封存下来，重新写一些有意义的文章，同时我也会用这个平台记录未来的生活，💞 with @lsq210。</p><a id="more"></a><p>不过从易用性角度出发，如何快速发布博客是个大问题…</p><h2 id="2024-09-09-更新"><a href="#2024-09-09-更新" class="headerlink" title="2024-09-09 更新"></a>2024-09-09 更新</h2><p>移动端不能配图，导致快速发布博客非常困难，可能需要一个自动上传图床的移动端编辑器，再一键转换为 markdown 格式的文章，再研究一下</p><h2 id="2024-09-26-更新"><a href="#2024-09-26-更新" class="headerlink" title="2024-09-26 更新"></a>2024-09-26 更新</h2><p>移动端写 markdown 还是不好操作，在 PC 端用飞书文档来写就很不错，最后用工具转为 markdown 格式的文件就可以使用了，参考 <a href="https://github.com/Wsine/feishu2md" target="_blank" rel="noopener">feishu2md</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;我打算把大学期间的技术博客封存下来，重新写一些有意义的文章，同时我也会用这个平台记录未来的生活，💞 with @lsq210。&lt;/p&gt;
    
    </summary>
    
      <category term="生活" scheme="https://cs-tao.github.io/blog/categories/%E7%94%9F%E6%B4%BB/"/>
    
    
      <category term="others" scheme="https://cs-tao.github.io/blog/tags/others/"/>
    
  </entry>
  
</feed>
