50、井字棋项目开发:从功能实现到测试与集成

井字棋项目开发:从功能实现到测试与集成

1. 项目起步与初始功能实现

在项目开始阶段,我们创建了 ConsoleGameView 类,其代码如下:

private TicTacToeGamePresenter _presenter;
public ConsoleGameView()
{
    _presenter =
        new TicTacToeGamePresenter(this, new TicTacToe(), 
                                   new PlainTextGameBoardRenderer());
    _presenter.start();
}
public void write_line(string message)
{
    System.Console.WriteLine(message);
}
public void get_coordinates_for_next_move()
{
    System.Console.ReadLine();
}
public void write(string message)
{
    System.Console.Write(message);
}

构建并运行测试用例,它们应该都能通过。此时,虽然已经能显示网格,但场景仍会失败,原因是缺少提示下一个玩家移动的信息。

2. 显示下一个玩家

显示下一个玩家移动的功能由游戏类负责。目前, TicTacToe 实例中硬编码为总是返回令牌 X 。为了更好地进行测试,我们创建了一个抽象基类 with_a_tictactoe_game :

using Wrox.BDD.Domain;
namespace Wrox.BDD.Specs.Core.Domain_Specs
{
    public abstract class with_a_tictactoe_game
    {
        public with_a_tictactoe_game()
        {
            SUT = new TicTacToe();
        }
        protected static Game SUT;
    }
}

接着,在 Domain_Specs 文件夹下创建了一个新的规范类 when_placing_a_token_on_the_board :

using Machine.Specifications;
using Rhino.Mocks;
using Wrox.BDD.Domain;
namespace Wrox.BDD.Specs.Core.Domain_Specs
{
    [Subject(typeof(TicTacToe))]
    public class when_placing_a_token_on_the_board : with_a_tictactoe_game
    {
        Establish context = () =>
        {
            coordinate_text = “1,1”;
            coordinate = Coordinate.parse(coordinate_text);
        };
        private Because of = () =>
        {
            SUT.place_token_for_current_player_at(coordinate);
        };        
        private It should_alternate_the_player = () =>
        {
            player_tracker.AssertWasCalled(x => x.finish_players_move());
        };
        private static Coordinate coordinate;
        private static string coordinate_text;    
    }
}

这个规范期望在玩家移动后调用 player_tracker 实例。 player_tracker 的职责是记录当前轮到谁,并在 finish_players_move 方法被调用后切换玩家。我们更新了 with_a_tictactoe_game 基类,将 player_tracker 作为构造函数的依赖项:

using Wrox.BDD.Domain;
using Rhino.Mocks;
namespace Wrox.BDD.Specs.Core.Domain_Specs
{
    public abstract class with_a_tictactoe_game
    {
        public with_a_tictactoe_game()
        {
            player_tracker = MockRepository.GenerateStub<PlayerTracker>();
            SUT = new TicTacToe(player_tracker);
        }
        protected static Game SUT;
        protected static PlayerTracker player_tracker;
    }
}

同时,创建了 PlayerTracker 接口:

namespace Wrox.BDD.Domain
{
    public interface PlayerTracker
    {
        Token current_player();
        void finish_players_move();
    }
}

修改 TicTacToe 类,使其接受 PlayerTracker 实例,并将 current_token 方法委托给 PlayerTracker :

namespace Wrox.BDD.Domain
{
    public class TicTacToe : Game
    {
        private PlayerTracker _player_tracker;
        public TicTacToe(PlayerTracker player_tracker)
        {
            _player_tracker = player_tracker;
        }
        public Token current_token()
        {
            return _player_tracker.current_player();
        }
        public void place_token_for_current_player_at(Coordinate coordinate)
        {
            _player_tracker.finish_players_move();
        }
    }
}

创建 TokenTracker 类来实现 PlayerTracker 接口:

namespace Wrox.BDD.Domain
{
    public class TokenTracker : PlayerTracker
    {
        private Token _current_playing_token = Tokens.x_token;
        public Token current_player()
        {
            return _current_playing_token;
        }
        public void finish_players_move()
        {
            _current_playing_token = 
                 (_current_playing_token.Equals(Tokens.x_token)) 
                                                   ? Tokens.o_token : 
                                                        Tokens.x_token;
        }
    }
}

最后,更新 FakeGameView 和 ConsoleGameView ,为 TicTacToe 实例提供 TokenTracker :

namespace Wrox.BDD.Specs.UAT
{
    public class FakeGameView : GameView
    {
        private TicTacToeGamePresenter _presenter;
        private StringBuilder _display = new StringBuilder(); 

        public FakeGameView()
        {
            _presenter = new TicTacToeGamePresenter(this, 
                                         new TicTacToe(new TokenTracker()), 
                                         new PlainTextGameBoardRenderer());
            GameStorage.presenter = _presenter;
            _presenter.start();
        }
    }
}
namespace Wrox.BDD.Ui.Console
{
    public class ConsoleGameView : GameView
    {
        private TicTacToeGamePresenter _presenter;
        public ConsoleGameView()
        {
            _presenter =
                new TicTacToeGamePresenter(this, new TicTacToe(new 
                     TokenTracker()), 
                                                 new PlainTextGameBoardRenderer());
            _presenter.start();
        }
    }
}

再次构建并运行所有测试用例,如果都通过,说明我们完成了这一阶段的功能开发。

3. 集成交替玩家场景

要集成交替玩家场景,只需更新 ConsoleGameView 类的 get_coordinates_for_next_move 方法,将用户输入传递给游戏:

namespace Wrox.BDD.Ui.Console
{
    public class ConsoleGameView : GameView
    {
        public void get_coordinates_for_next_move()
        {
            var coordinates = System.Console.ReadLine();
           _presenter.update_game_with_move(coordinates);    
        }
    }
}

现在可以运行代码并输入坐标,但需要注意输入的语法,因为还没有处理异常情况。

4. 显示游戏场景

接下来要处理的场景是在一系列移动后显示游戏。需要在 GamePlay.feature 文件中添加新的场景,并在 GamePlaySteps 类中添加新的步骤定义:

namespace Wrox.BDD.Specs.UAT.Steps
{
    [Binding]
    public class GamePlaySteps
    {
        [Given(@”the following moves are played:”)]
        public void GivenTheFollowingMovesArePlayed(Table table)
        {
            foreach (var row in table.Rows)
            {
                GameStorage.presenter.update_game_with_move(
                   string.Format(“{0},{1}”, row[“row”], row[“column”]));
            }
            GameStorage.game_view.clean_display();
        }
    }
}

为了支持游戏显示,需要添加以下几个行为:
1. 游戏应该将令牌放置在网格的方块上。
2. 游戏应该能够确认令牌的放置。
3. 游戏渲染器应该能够获取网格的只读视图进行渲染。

下面是这些行为的具体实现步骤:

4.1 放置令牌在网格上

更新 when_placing_a_token_on_the_board 规范:

namespace Wrox.BDD.Specs.Core.Domain_Specs
{
    [Subject(typeof(TicTacToe))]
    public class when_placing_a_token_on_the_board : with_a_tictactoe_game
    {
        Establish context = () =>
        {
            coordinate_text = “1,1”;
            coordinate = Coordinate.parse(coordinate_text);           
            player_tracker.Stub(x => x.current_player()).Return(Tokens.x_token);
        };
        private Because of = () =>
        {
            SUT.place_token_for_current_player_at(coordinate);
        };
        private It should_place_a_token_on_the_grid = () =>
        {
            tic_tac_toe_grid.AssertWasCalled(x => x.place_token_at(
                Arg<Coordinate>.Matches(c => c.Equals(coordinate)),
                Arg<Token>.Matches(c => c.Equals(Tokens.x_token))));            
        };
        private static Coordinate coordinate;
        private static string coordinate_text;    
    }
}

修改 with_a_tictactoe_game 基类,将 tic_tac_toe_grid 作为构造函数的依赖项:

namespace Wrox.BDD.Specs.Core.Domain_Specs
{
    public abstract class with_a_tictactoe_game
    {
        public with_a_tictactoe_game()
        {
            player_tracker = MockRepository.GenerateStub<PlayerTracker>();
            tic_tac_toe_grid = MockRepository.GenerateStub<Grid>();
            SUT = new TicTacToe(player_tracker, tic_tac_toe_grid);
        }
        protected static Game SUT;
        protected static PlayerTracker player_tracker;
        protected static Grid tic_tac_toe_grid;
    }
}

创建 Grid 接口:

namespace Wrox.BDD.Domain
{
    public interface Grid
    {
        void place_token_at(Coordinate coordinate, Token token);
    }
}

更新 TicTacToe 类,添加 Grid 作为构造函数的参数,并实现放置令牌的行为:

namespace Wrox.BDD.Domain
{
    public class TicTacToe : Game
    {
        private PlayerTracker _player_tracker;
        private Grid _grid;
        public TicTacToe(PlayerTracker player_tracker, Grid grid)
        {
            _player_tracker = player_tracker;
            _grid = grid;
        }
        public Token current_token()
        {
            return _player_tracker.current_player();
        }
        public void place_token_for_current_player_at(Coordinate coordinate)
        {
            _grid.place_token_at(coordinate, current_token());
            _player_tracker.finish_players_move();
        } 
    }
}

创建 NineSquareGrid 类来实现 Grid 接口:

namespace Wrox.BDD.Domain
{
    public class NineSquareGrid : Grid
    {
        public void place_token_at(Coordinate coordinate, Token token)
        {

        }
    }
}

更新 FakeGameView 和 ConsoleGameView ,为 TicTacToe 实例提供 NineSquareGrid :

namespace Wrox.BDD.Specs.UAT
{
    public class FakeGameView : GameView
    {
        private TicTacToeGamePresenter _presenter;
        private StringBuilder _display = new StringBuilder(); 

        public FakeGameView()
        {
            _presenter = new TicTacToeGamePresenter(this, 
                               new TicTacToe(new TokenTracker(),
                               new NineSquareGrid()), 
                               new PlainTextGameBoardRenderer());
            GameStorage.presenter = _presenter;
            _presenter.start();
        }
    }
}
namespace Wrox.BDD.Ui.Console
{
    public class ConsoleGameView : GameView
    {
        private TicTacToeGamePresenter _presenter;
        public ConsoleGameView()
        {
            _presenter =
                new TicTacToeGamePresenter(this,
                             new TicTacToe(new TokenTracker(), 
                             new NineSquareGrid()), 
                             new PlainTextGameBoardRenderer());
            _presenter.start();
        }
    }
}
4.2 确认令牌的放置

创建 when_checking_for_a_token_on_the_grid 规范:

using Machine.Specifications;
using Wrox.BDD.Domain;
using NUnit.Framework;
namespace Wrox.BDD.Specs.Core.Domain_Specs
{
    [Subject(typeof(NineSquareGrid))]
    public class when_checking_for_a_token_on_the_grid
    {
        Establish context = () =>
        {            
            coordinate_with_token = Coordinate.parse(“1,1”);
            coordinate_without_token = Coordinate.parse(“2,2”);
            SUT = new NineSquareGrid();
        };
        private Because of = () =>
        {
            SUT.place_token_at(coordinate_with_token, Tokens.x_token);
        };
        private It should_contain_a_token = () =>
        {
            Assert.That(SUT.contains_token_at(coordinate_with_token), Is.True);
        };
        private It should_not_contain_a_token = () =>
        {
            Assert.That(SUT.contains_token_at(coordinate_without_token), Is.False);
        };
        private static NineSquareGrid SUT;
        private static Coordinate coordinate_with_token;
        private static Coordinate coordinate_without_token;        
    }
}

修改 Grid 接口,添加 contains_token_at 方法:

namespace Wrox.BDD.Domain
{
    public interface Grid
    {
        void place_token_at(Coordinate coordinate, Token token);
        bool contains_token_at(Coordinate coordinate);
    }
}

更新 NineSquareGrid 类,实现 contains_token_at 方法:

namespace Wrox.BDD.Domain
{
    public class NineSquareGrid : Grid
    {
        protected readonly Token[,] _squares;
        public NineSquareGrid()
        {           
            _squares = new Token[3, 3];
        }
        public void place_token_at(Coordinate coordinate, Token token)
        {
            _squares[coordinate.X, coordinate.Y] = token;
        }
        public bool contains_token_at(Coordinate coordinate)
        {
            return _squares[coordinate.X, coordinate.Y] != null;
        }
    }
}
4.3 获取只读视图进行渲染

创建 when_rendering_the_game 规范:

using Machine.Specifications;
using Wrox.BDD.Domain;
using Wrox.BDD.Ui.Console.Presentation;
using Rhino.Mocks;
namespace Wrox.BDD.Specs.Core.Presentation_Specs
{
    [Subject(typeof(PlainTextGameBoardRenderer))]
    public class when_rendering_the_game
    {
        Establish context = () =>
        {
            var square_array = new string[3, 3];
            game = MockRepository.GenerateStub<Game>();
            game.Stub(x => x.get_game_view())
                       .Return(new GridView() { squares = square_array });
            SUT = new PlainTextGameBoardRenderer();
        };
        private Because of = () =>
        {
            SUT.render(game);
        };
        private It should_ask_the_game_for_a_readonly_view = () =>
        {
            game.AssertWasCalled(x => x.get_game_view());
        };
        private static PlainTextGameBoardRenderer SUT;
        private static Game game;
    }
}

更新 Game 接口,添加 get_game_view 方法:

namespace Wrox.BDD.Domain
{
    public interface Game
    {        
        Token current_token();
        void place_token_for_current_player_at(Coordinate coordinate);
        GridView get_game_view();
    }    
}

创建 GridView 类:

namespace Wrox.BDD.Domain
{
    public class GridView
    {
        public string[,] squares { get; set; }
    }
}

更新 TicTacToe 类,实现 get_game_view 方法:

namespace Wrox.BDD.Domain
{
    public class TicTacToe : Game
    {
        public GridView get_game_view()
        {
            return new GridView(){ squares = new string[3,3]};
        }
    }
}

更新 PlainTextGameBoardRenderer 类,实现渲染逻辑:

namespace Wrox.BDD.Ui.Console.Presentation
{
    public class PlainTextGameBoardRenderer : BoardRenderer 
    {
        public string render(Game game)
        {
            var squares = game.get_game_view().squares;
            var grid_display = new StringBuilder();
            var row_seperator = “”;
            for (int row = 0; row <= 2; row++)
            {
                if (!String.IsNullOrEmpty(row_seperator))
                    grid_display.AppendLine(row_seperator);
                var pipe = “”;
                var row_display = new StringBuilder();
                for (int column = 0; column <= 2; column++)
                {
                    row_display.Append(string.Format(“{0} {1} “, pipe,
                               pad_if_square_empty(squares[row, column])));
                    pipe = “|”;
                }
                grid_display.AppendLine(row_display.ToString());
                row_seperator = “-----------”;
            }
            return grid_display.ToString();
        }
        private string pad_if_square_empty(string value)
        {
            return String.IsNullOrEmpty(value) ? “ “ : value;
        }
    }
}
总结

通过以上步骤,我们逐步实现了井字棋游戏的多个功能,包括交替玩家、放置令牌、确认令牌放置和渲染游戏视图等。每个功能的实现都遵循了测试驱动开发的原则,通过编写规范和测试用例来确保代码的正确性。在开发过程中,我们还注重了代码的可维护性和可扩展性,将不同的功能模块分离,如游戏类、网格类和渲染器类等。

下面是整个开发过程的流程图:

graph TD;
    A[项目起步] --> B[显示下一个玩家];
    B --> C[集成交替玩家场景];
    C --> D[显示游戏场景];
    D --> D1[放置令牌在网格上];
    D --> D2[确认令牌的放置];
    D --> D3[获取只读视图进行渲染];

通过这个流程图,我们可以清晰地看到整个开发过程的步骤和顺序。每个步骤都有明确的目标和实现方法,确保了项目的顺利进行。

井字棋项目开发:从功能实现到测试与集成

5. 优化只读视图生成

虽然已经实现了获取只读视图进行渲染的功能,但生成只读视图的责任应该由网格实例承担。因此,我们创建了一个新的规范 when_generating_a_readonly_view_for_rendering :

using Machine.Specifications;
using Wrox.BDD.Domain;
using NUnit.Framework;
namespace Wrox.BDD.Specs.Core.Domain_Specs
{
    [Subject(typeof(NineSquareGrid))]
    public class when_generating_a_readonly_view_for_rendering
    {
        Establish context = () =>
        {
            coordinate_with_X_token = Coordinate.parse(“1,1”);
            coordinate_with_O_token = Coordinate.parse(“2,2”);
            SUT = new NineSquareGrid();
        };
        private Because of = () =>
        {
            SUT.place_token_at(coordinate_with_X_token, Tokens.x_token);
            SUT.place_token_at(coordinate_with_O_token, Tokens.o_token);
            result = SUT.generate_grid_view();
        };
        private It should_have_the_X_token_in_the_correct_position = () =>
        {
            Assert.That(result.squares[coordinate_with_X_token.X, 
                                     coordinate_with_X_token.Y], 
                                     Is.EqualTo(Tokens.x_token.value));
        };
        private It should_have_the_O_token_in_the_correct_position = () =>
        {
            Assert.That(result.squares[coordinate_with_O_token.X, 
                                      coordinate_with_O_token.Y], 
                                      Is.EqualTo(Tokens.o_token.value));
        };
        private static GridView result;
        private static NineSquareGrid SUT;
        private static Coordinate coordinate_with_X_token;
        private static Coordinate coordinate_with_O_token;
    }
}

修改 Grid 接口,添加 generate_grid_view 方法:

namespace Wrox.BDD.Domain
{
    public interface Grid
    {
        void place_token_at(Coordinate coordinate, Token token);
        bool contains_token_at(Coordinate coordinate);
        GridView generate_grid_view();
    }
}

更新 NineSquareGrid 类,实现 generate_grid_view 方法:

namespace Wrox.BDD.Domain
{
    public class NineSquareGrid : Grid
    {
        protected readonly Token[,] _squares;
        public NineSquareGrid()
        {           
            _squares = new Token[3, 3];
        }
        public void place_token_at(Coordinate coordinate, Token token)
        {
            _squares[coordinate.X, coordinate.Y] = token;
        }
        public bool contains_token_at(Coordinate coordinate)
        {
            return _squares[coordinate.X, coordinate.Y] != null;
        }
        public GridView generate_grid_view()
        {
            var readonly_squares = new string[3,3];
            for (int row = 0; row < 3; row++)
                for (int column = 0; column < 3; column++)
                  readonly_squares[row, column] = 
                                 _squares[row, column] == null ? 
                                 “” : _squares[row, column].value;
            return new GridView() {squares = readonly_squares};
        }
    }
}

最后,更新 TicTacToe 类,将生成只读视图的操作委托给网格:

namespace Wrox.BDD.Domain
{
    public class TicTacToe : Game
    {
        private PlayerTracker _player_tracker;
        private Grid _grid;
        public TicTacToe(PlayerTracker player_tracker, Grid grid)
        {
            _player_tracker = player_tracker;
            _grid = grid;
        }
        public Token current_token()
        {
            return _player_tracker.current_player();
        }
        public void place_token_for_current_player_at(Coordinate coordinate)
        {
            _grid.place_token_at(coordinate, current_token());
            _player_tracker.finish_players_move();
        } 
        public GridView get_game_view()
        {
            return _grid.generate_grid_view();
        }
    }
}
6. 开发进度总结与展望

目前,我们已经完成了井字棋游戏的多个核心功能,包括交替玩家、放置令牌、确认令牌放置和渲染游戏视图等。以下是对已实现功能的总结表格:
| 功能模块 | 实现内容 |
| ---- | ---- |
| 交替玩家 | 创建 PlayerTracker 接口和 TokenTracker 类,实现玩家交替逻辑 |
| 放置令牌 | 创建 Grid 接口和 NineSquareGrid 类,实现令牌放置功能 |
| 确认令牌放置 | 在 Grid 接口和 NineSquareGrid 类中添加 contains_token_at 方法,确认令牌放置 |
| 渲染游戏视图 | 创建 GridView 类,在 Game 接口和 TicTacToe 类中添加 get_game_view 方法,在 PlainTextGameBoardRenderer 类中实现渲染逻辑 |

在开发过程中,我们遵循了测试驱动开发(TDD)的原则,通过编写规范和测试用例来确保代码的正确性。同时,我们注重代码的可维护性和可扩展性,将不同的功能模块分离,如游戏类、网格类和渲染器类等。

然而,项目仍有一些待完善的地方。例如,目前还没有处理输入坐标的异常情况,也没有实现游戏结束的判定逻辑,如判断是否有玩家获胜或平局。以下是后续开发的计划列表:
1. 异常处理 :添加对输入坐标的验证,处理无效输入的情况。
2. 游戏结束判定 :在 TicTacToe 类中添加方法,判断是否有玩家获胜或平局。
3. 用户界面优化 :可以考虑使用更美观的界面库来替换现有的纯文本渲染器。

下面是包含后续开发计划的流程图:

graph TD;
    A[已完成功能] --> B[异常处理];
    A --> C[游戏结束判定];
    A --> D[用户界面优化];

通过这个流程图,我们可以清晰地看到后续开发的方向。每个待完成的任务都有明确的目标,有助于我们有条不紊地推进项目的进一步完善。

7. 代码复用与扩展建议

在整个开发过程中,我们将不同的功能模块进行了分离,这为代码的复用和扩展提供了便利。以下是一些代码复用和扩展的建议:

7.1 代码复用
  • Grid 接口和 NineSquareGrid 类 :如果要开发其他基于网格的游戏,如五子棋或围棋,可以复用 Grid 接口和 NineSquareGrid 类的基本逻辑,只需根据具体游戏规则进行适当修改。
  • PlayerTracker 接口和 TokenTracker 类 :对于需要交替玩家的游戏,都可以复用这部分代码来实现玩家交替的功能。
7.2 代码扩展
  • 添加新的渲染器 :如果需要支持不同的显示方式,如图形界面或网页显示,可以创建新的渲染器类,实现 BoardRenderer 接口。
  • 扩展游戏规则 :可以在 TicTacToe 类中添加新的方法,实现更复杂的游戏规则,如增加棋盘大小或改变获胜条件。

通过合理的代码复用和扩展,我们可以在现有代码的基础上快速开发新的游戏或功能,提高开发效率。

8. 总结

通过本次井字棋项目的开发,我们深入实践了测试驱动开发的方法,从项目起步到各个功能的逐步实现,再到后续的优化和扩展规划,每一步都有明确的目标和实现方法。我们不仅实现了一个完整的井字棋游戏,还注重了代码的质量和可维护性。

在未来的开发中,我们可以借鉴本次项目的经验,将功能模块分离、编写规范和测试用例等方法应用到其他项目中,提高项目的开发效率和代码质量。同时,不断完善和扩展现有项目,为用户提供更好的游戏体验。

相关推荐

井字棋图建模:用Network Graph Data Modeling实现可解释AI决策

图数据建模(Graph Data Modeling)是一种将现实关系转化为节点、边与属性的结构化表达方法,其核心原理在于通过拓扑结构承载业务逻辑而非依赖黑盒计算。相比传统搜索算法,它以可解释性、状态解耦和增量推理为技术价值,在游戏AI、规则引擎、供应链风控等需强逻辑追溯的场景中优势显著。本文以井字棋为载体,深入实践Network Graph Data Modeling——不使用Minimax,而是构建含真实节点与辅助裁判节点的有向图,将胜负判定转化为入度查询与属性聚合,使‘哪步必胜’可被图中心性、连通性与动

congli3478的博客 439

电赛井字棋无视觉方案:STM32按键矩阵与LED点阵实现

状态机是嵌入式系统开发中的核心设计模式,通过定义有限状态和状态转移条件来实现复杂的控制逻辑。其原理是将系统行为分解为离散的状态,每个状态对应特定的处理函数和迁移路径。在电子设计竞赛等实践场景中,状态机能够有效管理游戏流程、传感器数据和用户交互。结合按键矩阵输入检测和LED点阵显示输出,可以构建可靠的人机交互闭环。本文以电赛井字棋题目为例,详解基于STM32的无视觉方案实现,涵盖硬件选型、状态机设计和调试要点,帮助初学者掌握嵌入式开发全流程。

weixin_34246551的博客 461

用OpenSpiel打造你的第一个强化学习游戏AI:从井字棋到实战

本文详细介绍了如何使用OpenSpiel工具库从零开始构建强化学习游戏AI,涵盖从井字棋基础实现到Q-Learning、蒙特卡洛树搜索等高级算法的实战应用。通过清晰的代码示例和系统架构设计,帮助开发者快速掌握游戏AI开发的核心技术,并扩展到复杂游戏场景。

weixin_30892037的博客 39

Python趣学篇:从零打造智能AI井字棋游戏(Python + Tkinter + Minimax算法)

还记得小时候在纸上玩的井字棋吗?三横三竖的格子,谁先连成一线谁获胜。这个看似简单的游戏,其实蕴含着丰富的算法智慧。今天我们要*Python打造一个智能井字棋游戏,不仅有漂亮的图形界面,还搭载了聪明的AI对手!

Code_流苏:在代码中寻诗意,在实践中觅真知 2111

SAP License USMM SLAW2 SAP审计

引言 企业每年都会收到SAP License审计邮件,邮件内容大致就是根据贵公司与SAP签定的合同XXX条款规定,贵公司所安装的SAP系统需要进行测定。 关键字: SAP License、 SAP USMM、 SLAW2、 SAP 账号、SAP 账号审计、 SAP License 审计、 SAP 审计报告、SAP 账号优化 SAP License审计邮件 企业每年都会收到SAP License审计邮件,邮件内容大致就是根据贵公司与SAP签定的合同XXX条款规定,贵公司所安...

感悟人生 2975

带硬约束的物理信息神经网络(PINN with Hard Constraint)示例及代码

本文以一个案例介绍了带硬约束的物理信息神经网络的原理及实现步骤。

m0_72768523的博客 5356

英语语法-句法

摘要:本文系统梳理了英语句法知识,重点解析了五种基本句型及其扩展结构。内容包括句子类型(陈述句、疑问句、祈使句、感叹句)、复合句构成(从句分类与连词使用)、特殊句型(倒装、强调、引语转换)等。特别强调句子成分分析技巧,指出每种句子成分可由不同词法单元充当,需掌握成分识别与划分方法。文章还提供了往期语法系列链接,形成完整的英语语法学习体系,帮助读者建立从词法到句法的系统认知框架。

STCNXPARM的博客 8795

VCSA 6.7证书过期别慌!手把手教你通过SSH修改系统时间恢复登录(附STS证书修复脚本)

本文详细介绍了VCSA 6.7证书过期的应急处理方案,包括通过SSH修改系统时间临时恢复登录、全面更新证书体系以及专项修复STS证书的实战步骤。文章还提供了后期验证和系统加固的建议,帮助运维人员有效应对证书过期问题,确保虚拟化平台的稳定运行。

weixin_31292729的博客 414

视频倍速 - 在线调倍速的加速器APP工具

一刀工具箱APP提供一键可将手机上的视频倍速播放,可以调整视频倍速的加速器,调节视频的速度也就是调节视频的播放速度!一键可将手机上的视频倍速播放,可以调整视频倍速的加速器,调节视频的速度也就是调节视频的播放速度!视频倍速-在线调倍速的加速器-一刀工具箱APP。...

无涯教程 7553

同物种,不同版本之间的坐标转化

是最常用的工具,如果有可用的链文件,这通常是最便捷的方法。CrossMap是一个功能强大的替代工具,支持多种文件格式和物种。自定义生成链文件适用于没有现成资源的情况,虽然复杂一些,但可以满足特定需求。Ensembl 和 Gramene提供的工具适用于支持的物种,尤其在植物基因组领域可能更具优势。选择合适的方法取决于你正在处理的物种、基因组版本以及现有的资源。

qq_40256654的博客 1851

基于 ESP32 的智能药盒提醒装置设计与实现

本装置基于 ESP32 实现了智能药盒的核心功能,硬件选型兼顾成本与实用性,软件设计模块化、易拓展。后续可通过 ESP32 的 WiFi 模块接入物联网平台(如阿里云、OneNet),实现远程设置提醒、服药状态反馈等功能,进一步提升智能化水平。该装置能有效解决老人忘服药问题,具有较高的实用价值与推广潜力。

weixin_42300449的博客 1042

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

上一篇: 1、基于图投影的半监督聚类与知识管理应用
下一篇: 33、无损数据隐藏与指纹同步技术研究
corn8
博客等级 码龄1天 0粉丝 1639原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值