📜 ⬆️ ⬇️

Your move, comrade .NET, or Reversi again under nanoCAD

Some time ago we had a big event - release of the nanoCAD 3.5 release. The key innovation of this version was the open API, which will be discussed in this article.

As you know, the best way to learn something is to do it. Once I wrote Reversi under nanoCAD on a script. Now I decided to write Reversi on .NET.
nanoCAD_MgdReversi
The result is a cross-CAD-platform application that can work not only under nanoCAD. How it was done - look under the cut.

It was possible to program under nanoCAD before. Dows wrote Sierpinski's curves on dows scripts, I wrote Reversi , there were other examples from our forum. This is all, of course, good, but not enough. Therefore, my next move is .NET.

Entry level.


The first thing to do was create an assembly containing the code executed in nanoCAD:

The method that will be registered as a command must have a public modifier and be marked with the special attribute CommandMethod.
')
For example, HelloWorld looks like this:
[CommandMethod("HelloWorld")] public void HelloWorld () { Editor ed = Platform.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor; //      ed.WriteMessage("     nanoCAD!"); } 

AND EVERYTHING!
I do not write about this in more detail, since this can be read in the nanoCAD SDK. Where to get? In the nanoCAD Developer Club , registration is open.

Structure.


I divided the game into several classes: game class, game board class, information panel class, game chip class:
Each class should be as independent as possible.
Next, I needed to learn how to create objects, change them, and communicate with the user.

Creating objects. Mat. part.


Before drawing reversi, it was necessary to understand what to do, what to undertake.
In order to create objects, you need to know a little about the structure of the document. Each document has a database. The database stores the objects contained in the drawing and their relationships with each other. Everything is stored in the database: lines with arcs, model space, text styles and much more. Adding a new object to the drawing, you need to add it to the database. And where there is a database, there are transactions.

Transactions are needed to protect our document: if the result of the code execution failed, the objects added by this code would not fall into the document - the transaction will be canceled. If everything completes successfully, the transaction will be confirmed and the objects will be added.

 Database db = Application.DocumentManager.MdiActiveDocument.Database; TransactionManager tm = db.TransactionManager; using (Transaction tr = tm.StartTransaction()) { ... tr.Commit(); } 

Just create an object a little. It will not go anywhere and hang "in the air." The object needs to be placed somewhere. This is usually a model space. There was something similar in the scripts - he said to the model space “make a line” - it will appear there. In .NET, a little differently - you need to add the created object to the model space and to the transaction.

 using (Transaction tr = tm.StartTransaction()) { BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead, false) as BlockTable; BlockTableRecord ms = tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite, false) as BlockTableRecord; Line line = new Line(); ObjectId lid = ms.AppendEntity(line); //     tr.AddNewlyCreatedDBObject(line, true); //    tr.Commit(); //   } 

Each object added to the database is uniquely identified by its personal code - ObjectId. Using ObjectId, you can open objects for reading or writing.

Creating objects 2. Full speed ahead.


Fine. Armed with knowledge of the internal kitchen of the document, you can finally begin to develop a game board class. No board - no party. So the first thing I started doing was drawing cells in the document space.

I made the cells from the hatching. Having opened the description of the Hatch object in NCadSDK.chm (the documentation is included in the SDK, which is available to the members of the Developer Club ), I learned the knowledge I needed. The third paragraph immediately informed me that the hatching consists of loops, and the list of methods of the hatching object was suggested by the magic word AppendLoop (). That's what I need, I thought.

So, I built every cell from a square polyline that I painted over the hatching. All hatching together formed a square of 8 by 8 cells.

Next - on the thumb, everything is like last time: I create borders and tiles from 3Dmesh objects. Border is a polygon 2 by 2 vertices. I calculate the coordinates of the vertices, create them, add them to the network, add the network to the model.

 using (Transaction tr = tm.StartTransaction()) { //   PolygonMesh mesh = new PolygonMesh(); mesh.NSize = 2; mesh.MSize = 2; ms.AppendEntity(mesh); tr.AddNewlyCreatedDBObject(mesh, true); //     AddVertexToMesh(mesh, new Point3d(col*gridstep, 0,-linehight), tr); AddVertexToMesh(mesh, new Point3d(col*gridstep, 0, linehight), tr); AddVertexToMesh(mesh, new Point3d(col*gridstep,8*gridstep,-linehight), tr); AddVertexToMesh(mesh, new Point3d(col*gridstep,8*gridstep,linehight), tr); tr.Commit(); } //    private void AddVertexToMesh(PolygonMesh PolyMesh, Point3d Pt3d, Transaction Trans) { PolygonMeshVertex PMeshVer = new PolygonMeshVertex(Pt3d); PolyMesh.AppendVertex(PMeshVer); Trans.AddNewlyCreatedDBObject(PMeshVer, true); } 

Fine. There are cells, there are dividers. Drawing a chip is now also not difficult. I took the formulas for calculating the coordinates of the ball vertices from the script version of the game. True, I corrected them so that the object looked more like a gaming reversi chip.

Here's what I got as a result:
Game board

"I am fifty third, I go out on the square."


Now you need to learn how to respond to user actions.

Here again it is necessary to mention about the mat. part. In addition to the database, there are several other objects that relate not to the document itself, but to the application as a whole. This is, for example, an Application object, a collection of all documents opened in a DocumentCollection. And this is the user interaction object - Editor. There are others, but I don’t touch them now.

The Editor object has a number of methods for interacting with the user: query objects, query strings, numbers, regions. The object is requested by the GetEntity method (PromptEntityOptions). The PromptEntityOptions object is optional. The invitation string, keywords (if needed) are set through this object, and restrictions are imposed on the choice of objects. A similar object is accepted by all input methods.

The principle of the course remains the same - the user selects the cell where he wants to go. A cell is a hatching object. Therefore - I specify that we accept only hatch objects as input, an empty choice is to forbid, there must be an object. And write the invitation string.

 Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; ObjectId selectObj = ObjectId.Null; PromptEntityOptions opts = new PromptEntityOptions(" . "); opts.SetRejectMessage("\n    ."); opts.AddAllowedClass(typeof(Hatch), false); PromptEntityResult pr = ed.GetEntity(opts); 

The cell determines exactly where the user wants to place his chip. Further, the algorithm checks whether this can be done, if yes - the player moves and the necessary pieces are turned over.

Repainting existing chips.


As I already wrote - all objects live inside the database. This means that in order to read or change the properties of an object, this object must be opened. Opening objects is done using the GetObject () transaction method. When the change is complete, the transaction is confirmed.

 using (Transaction myT = db.TransactionManager.StartTransaction()) { // pieceId –  id     //   pieceId   - OpenMode.ForWrite PolygonMesh piece = myT.GetObject(this.pieceId, OpenMode.ForWrite) as PolygonMesh; //         piece.Color = (player == ePlayer.Human) ? Constants.HumanColor: Constants.PcColor; //   myT.Commit(); } 

Yummy.


I made two data structures for storing the game board in memory: an array and a dictionary.
The array stores the image of the board 8 to 8, and the matching dictionary cell element - ObjectId-hatching. Both data structures store references to game board objects. With this approach, you can not take care of synchronization. Only the Piece element will change. And you can always get it on the link. It does not matter from the array or from the dictionary.
 Dictionary<ObjectId, Piece> GameDesc = new Dictionary<ObjectId, Piece>(); Piece[,] GameDesc_xy = new Piece[8, 8]; 

Unlike scripts, on .NET I managed to make many things more beautiful and easier. The capabilities of the framework carried pleasant delicacies. For example, using LINQ, data structures were processed almost by themselves. Counting the number of user chips - in one line. Choosing a cell for the computer run - one request. Beauty.
 int GetCounterCount(ePlayer player) { //    player return gamedesk.GameDesc.Where(x => x.Value.Player == player).Count(); } 

Game process

Compiling and running the game


The sources of the game can be found here . You need to open the project in Visual Studio or SharpDeveloper and compile. Project paths are configured with the expectation that nanoCAD is installed in the standard directory.
If you do not need the source code, but you just want to look at the reversi, you can download the module we have assembled.

To start the game, you need to load the MgdReversi.dll assembly into nanoCAD with the NETLOAD command. Now you can run the game team PLAY.

What did not have time to do.


It would be interesting to stop in the middle of the game, save the game to a file in nanoCAD, open the file in AutoCAD and finish it, because the file format in both systems is the same.

But, for this you need to remake the architecture of the application, now information about the state of the game is stored in the team’s memory, and you need to save it in drawing objects (field, chips) that are saved to a file. Let's leave it for the future.

Until then, you can play Reversi without stopping, from the beginning to the end of the game, under AutoCAD, under nanoCAD, and there, and there the game works the same. It is enough to rebuild Reversi for AutoCAD using its SDK, ObjectARX, it’s not difficult.

Source: https://habr.com/ru/post/135173/


All Articles