Your move .NET or Reverci for nanoCAD

Your move .NET, or Reversi for nanoCAD 

nanoCAD 3.5 has an open API, it is a cool feature. I'm going to tell you how to use nanoCAD API in this article.
I'm not a professional programmer... But as you know, the best way to study something is to make it. I have decided to write Reversi game for nanoCAD in .NET.
The result is a CAD Reversi application, able to work not only in nanoCAD. It is a sort of 'cross-platform' development if your 'platform' is a CAD program capable to load .NET applications, such as nanoCAD, AutoCAD etc. 
     


The beginning

I use MS Visual C# 2008 Express Edition. It is free as is nanoCAD. You can download it from the Microsoft site.
At first, you have to create an assembly, containing the code, performed by nanoCAD:
  • Create a project: Visual C#, Class Library, 
  • Add the nanoCAD .NET libraries into the References: hostdbmgd.dll, hostmgd.dll, 
  • Register the command in nanoCAD. 

A method, registered as a command, must be defined as public and be marked with a special attribute - CommandMethod.

For example, HelloWorld is: 
 
[CommandMethod("HelloWorld")]
public void HelloWorld ()
{
Editor ed = Platform.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor;

// Enters a message in the command line
ed.WriteMessage("Welcome to the managed code of nanoCAD!");
}

That's all!
I will not write about it in details, because you can read it in the nanoCAD SDK. Where can you get nanoCAD SDK? In the nanoCAD developers club. Register as a nanoCAD developer and download SDK for free.

Structure

I have decided to divide the game into several classes: the Game class, the Game Board class, the Information Panel class and the Game Piece class:
  • Game class contains algorithms, checking the opportunity to make a move along the specified coordinates, searching the computer’s move, calculating players’ pieces and making a decision to continue a game 
  • Game board class draws the board and stores its content 
  • Information panel class shows results of the game 
  • Piece class draws a disk, changes its color and stores all information about certain game cells. 
Every class should be maximally independent. 
After that we have to study how to create objects, modify them and communicate with a user.

Creation of objects 1. Theory

It is a good idea to think a little bit before the beginning of actual programming.
You have to understand the structure of a nanoCAD drawing (document) as the first step. Each document has a database. There are drawing objects and their relationships to each other in the database. Everything is stored in the database: lines, arcs, model space, text styles, etc. When you add a new object to the drawing, you have to add it into the database. If there is a database, there should be transactions.

Transactions are required to protect a document: if there is a failure during the code implementation, objects, added by this code will not be added into the document and the transaction will be canceled. If everything is finished successfully a transaction will be confirmed and objects will be added into a document.

Database db = Application.DocumentManager.MdiActiveDocument.Database;
TransactionManager tm = db.TransactionManager;

using (Transaction tr = tm.StartTransaction())
{
...
tr.Commit();
}

Just creating an object is not enough. It will be disconnected and hung in the air. An object needs to be put somewhere as usual, in the model space. If you draw a line by a script in the model space, it will be drawn. It is a little bit different in .NET - you have to add a created object into the model space and to a 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); // adds to the model space
tr.AddNewlyCreatedDBObject(line, true); // and to a transaction

tr.Commit(); // save changes
}

Every object, added to the database, is determined by its individual code — ObjectId. Using the ObjectId you can open objects for reading and writing.

Creation of objects 2. Practice

Great, now we have studied about the internal structure of a document, and we are ready to start developing the Game Board class. If there is no game board, there is no game. That is why the first what I started to do was drawing the cells in the document space.

I make cells from hatches. I have found the required information about the Hatch object in the NCadSDK.chm (documentation belongs to SDK, available for members of the Developers Club). The third paragraph has told me that the hatch consists of loops, and a list of hatch object methods has suggested a magical word - AppendLoop(). It was what I needed.

So, I create each cell from a hatched square polyline. All hatches together are a Game Bord (8x8 cells).

After that I do everything as I did before: cell borders and disks are created from 3Dmesh objects. A cell border is a polygon of 2x2 vertexes. I calculate coordinates of vertexes and add them to a network, and add a network to a model space.

using (Transaction tr = tm.StartTransaction())
{
// create a cell border
PolygonMesh mesh = new PolygonMesh();
mesh.NSize = 2;
mesh.MSize = 2;
ms.AppendEntity(mesh);
tr.AddNewlyCreatedDBObject(mesh, true);

// create and add vertexes
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();
}

// add vertex to mesh
private void AddVertexToMesh(PolygonMesh PolyMesh, Point3d Pt3d, Transaction Trans)
{
PolygonMeshVertex PMeshVer = new PolygonMeshVertex(Pt3d);
PolyMesh.AppendVertex(PMeshVer);
Trans.AddNewlyCreatedDBObject(PMeshVer, true);
}

Ok, cells and cell borders are created.
The creation of a game disk class (I named it as 'Piece') is not very difficult. You can make them as spheres. I took formulas to calculate coordinates of sphera vertexes from Wikipedia. I then modify them a little to make them look like real Reversi game disks.
This what I get as a result:
     

Algorithm

Now we have to learn how to react to users’ actions.

I have to mention the theory again. Besides the database, there are several objects, which don’t belong to a document but to the application. For example, the Application object, DocumentCollection – a collection of all documents, opened in the application. The Editor object – an object of interaction with a user. There are other objects but they are not a part of the game.

The Editor object has a set of methods to interact with a user: a request for an object, for a string, for a value and for an area. A request for an object is performed by the GetEntity(PromptEntityOptions) method.
The PromptEntityOptions object contains optional parameters. This object specifies the prompt line, keywords (if required), and limits of object selection. Such objects accept all methods of input.

Rules of the game are: a user selects a cell for a move. The cells are a Hatch objects. We accept only Hatch objects as input objects, empty selections must be rejected. Then we are ready to write a prompt line. 
 
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;

ObjectId selectObj = ObjectId.Null;
PromptEntityOptions opts = new PromptEntityOptions("Your turn. Select a cell");
opts.SetRejectMessage("\nOnly a cell can be selected.");
opts.AddAllowedClass(typeof(Hatch), false);

PromptEntityResult pr = ed.GetEntity(opts);


By using a cell we determine where a user wants to move a piece. After that the algorithm determines whether or not it can be done, and if it is possible, the player’s move is performed and pieces are turned.

How to change disk color

 As I have written before all objects live inside the database. This means, that an object needs to be opened for reading and modifying its properties. Objects are opened by the GetObject() transaction method. After all changes are made the transaction is confirmed.

using (Transaction myT = db.TransactionManager.StartTransaction())
{
// pieceId – id of repainted piece in the DB
// opens the pieceId object for changes - OpenMode.ForWrite
PolygonMesh piece = myT.GetObject(this.pieceId, OpenMode.ForWrite) as PolygonMesh;

// assigns color according to whose the piece is
piece.Color = (player == ePlayer.Human) ? Constants.HumanColor: Constants.PcColor;

// confirms transaction
myT.Commit();
}

Something sweet

I make two structures of data to store the game board in the memory: an array and a dictionary.
The array stores the image of the board 8x8, and the  dictionary stores a relationship between cell element (Piece) and ObjectId of the hatch. Both structures store links to the objects of the game board. In such case you do not need to worry about synchronization. Only the Piece element is modified. You can always get it using the link. Iit does not matter whether you get it from the array or  dictionary.

Dictionary<ObjectId, Piece> GameDesc = new Dictionary<ObjectId, Piece>();
Piece[,] GameDesc_xy = new Piece[8, 8];


I have managed to do a lot of things easily with .NET. Framework its capabilities make me feel happy. For example, if you use LINQ, the structures of the database are processed by themselves. Counting of a player's disks is a single line of code. Selection of a cell for a computer’s move is just one request. Pure beauty.

int GetCounterCount(ePlayer player)
{
// calculation of player’s pieces
return gamedesk.GameDesc.Where(x => x.Value.Player == player).Count();
}

Compilation and launch of the game

If you are feeling lazy you can download my sources of the game from here. You have to open the project in Visual C# 2008 Express, Visual Studio 2008 or in SharpDeveloper and compile it. Project paths are set for nanoCAD installed in the default directory. Set Configuration in the Configuration Manager to Debug NCAD or Release NCAD.
If you do not need the source file but just want to play Reversi, you can download pre-built Reversi DLL from here.

To start the game you have to load MgdReversi.dll in nanoCAD using the NETLOAD command. Start the game with the PLAY command.

What remains to be done

It would be interesting to stop in the middle of the game, save the game into a dwg-file in nanoCAD, open this dwg-file in AutoCAD and finish the game, as both systems have the same file format.
     


But for this we have to rebuild the architecture of the application. In the current version game state information is stored in the command memory, but it should be stored in the objects of the drawing (board, disks), which can be saved into a dwg-file. It is a bit of job to be done. Let’s leave it for the future. 

You can play in the Reversi in nanoCAD without stopping, from the start to the end. Also it is not difficult to rebuild the Reversi for AutoCAD from the same sources, using ObjectARX SDK.
The game works in the same way either in nanoCAD or in AutoCAD. 
Copyright 2017 Nanosoft. All Rights Reserved. Privacy policy  |  Terms of use  |  Forgot password?
Follow us: