Sir Penguin

12-05-2005, 22:15:24

/* filename: sp_nim_game author: Neil MacMillan

* project: Game of Nim modification date: 11.05.05

* description: Game logic for the Game of Nim as implemented in a

* Neverwinter Nights module. There are a few other small scripts that

* actually use this one. They're called sp_nim_*. The sp_nim_reload script

* sets up the game area, although it makes some assumptions about the area

* geometry, but that's a story for another time.

*/

#include "zep_inc_main" // CEP stuff for ToggleTorch()

int NUM_COLS = 16; // the number of torches in a row

/* int GetNimXOR();

* Return the 3-way bitwise exclusive OR of the three row values

* Type: Calculation

* Output: int, the bitwise XOR of the three row values.

*/

int GetNimXOR()

{

int a = GetLocalInt(GetObjectByTag("NIM_PORTAL"),"nRow1Val");

int b = GetLocalInt(GetObjectByTag("NIM_PORTAL"),"nRow2Val");

int c = GetLocalInt(GetObjectByTag("NIM_PORTAL"),"nRow3Val");

return a^b^c;

}

/* int GetNimMove(int a, int b, int c);

* Figure out how many torches to keep lit in the row with value c.

* Type: Calculation

* Input: int a, the value of a safe row; int b, the value of a safe row; int c,

* the value of the row from which to subtract something.

* Output: the new value of c (the number of torches to leave in that row)

*/

int GetNimMove(int a, int b, int c)

{

int nNewC = a^b; // the number of torches to remove

if ((nNewC^(a^b)) != 0) {

SpeakString("Error in GetNimMove, XOR check = "+IntToString(nNewC^(a^b)));

} else if (nNewC <= 0) {

SpeakString("Error in GetNimMove, nNewC = "+IntToString(nNewC));

}

return nNewC;

}

// the next two functions aren't really needed.

/* string IntToBitString(int nNum);

* Convert a non-negative integer into a bit string.

* Type: Wrapper

* Input: int nNum, the integer to convert

* Output: the bit string representation of nNum, "0" if nNum is 0.

*/

string _IntToBitStringCalc(int nNum);

string IntToBitString(int nNum)

{

if (nNum == 0) {

return "0";

} else {

return _IntToBitStringCalc(nNum);

}

}

/* string IntToBitStringCalc(int nNum);

* Convert a *positive* integer into a bit string.

* Type: Calculation

* Input: int nNum, the non-negative, non-zero integer to convert

* Output: the bit string representation of nNum.

*/

string _IntToBitStringCalc(int nNum)

{

//return ( (n > 0) && (_IntToBitString(n>>>1)+IntToString(n&1)) || "0");

if (nNum > 0) {

return _IntToBitStringCalc(nNum >>> 1) + IntToString(nNum & 1);

} else {

// if this condition returns "0", the output bit string will have a

// leading 0. That's undesireable, and so is outputting an empty string

// on a non-recursed 0 input, so this function is placed in a wrapper

// that deals with 0 inputs (above). Better solutions?

// Better solution: not using the fucking thing. Argh.

return "";

}

}

/* NimTriMax(int a, int b, int c);

* Return the maximum of three integers. Equal values are handled properly.

* Type: Calculation

* Input: int a, an integer to compare; int b, an integer to compare; inc c,

* an integer to compare

* Output: the maximum of the three integers

*/

int NimTriMax(int a, int b, int c)

{

if (a > b && a > c) return a;

if (b > c) return b;

return c;

}

/* Adapted from zep_torch, Copyright (c) 2001 Bioware Corp.

* Originally modified by Dan Heidel 1/21/04 for CEP

* More modifications by Neil MacMillan 09.05.05 for Game of Nim

* I think all changes have been marked.

*/

// This could be pruned for our purposes--we don't need to turn a light on.

/* void ToggleTorch(object oTorchVictim);

* Turn a torch on if it's off, or off if it's on. This is done by replacing

* a torch that's on with one that's off and vice versa.

* Type: Replace Object

* Input: object oTorchVictim, the torch to toggle

*/

void ToggleTorch(object oTorchVictim)

{

// these were changed to refer to oTorchVictim instead of OBJECT_SELF.

location lLoc = GetLocation(oTorchVictim);

string sResRef = GetResRef(oTorchVictim);

int nAmIOn = GetLocalInt(oTorchVictim, "CEP_L_AMION");

int nLightCycle = GetLocalInt(oTorchVictim, "CEP_L_LIGHTCYCLE");

int nInitialized = GetLocalInt(oTorchVictim, "CEP_L_LIGHTINITIALIZED");

int nLightDiurnal = GetIsNight();

string sLightConst = GetLocalString(oTorchVictim, "CEP_L_LIGHTCONST");

string sLightSwap = GetLocalString(oTorchVictim, "CEP_L_LIGHTSWAP");

int nLight = ColorInit(sLightConst);

// I changed this because this way's more efficient and easier to read.

// Although now that I think about it, some math nerd might think it's

// finding the derangement of nAmIOn, but since the derangement of a set

// of size 1 is zero, well, it works out anyway since we're only switching

// from 1 to 0. Hah, suck it, math.

nAmIOn = !nAmIOn;

object oNew = CreateObject(OBJECT_TYPE_PLACEABLE, sLightSwap, lLoc);

SetLocalInt(oNew, "CEP_L_AMION", nAmIOn);

// the CEP_L_LIGHTCYCLE setting is changed, because we want the torch to be

// off all the time.

SetLocalInt(oNew, "CEP_L_LIGHTCYCLE", 0);

SetLocalInt(oNew, "CEP_L_LIGHTINITIALIZED", nInitialized);

SetLocalInt(oNew, "CEP_L_LIGHTDIURNAL", nLightDiurnal);

SetLocalString(oNew, "CEP_L_LIGHTCONST", sLightConst);

SetLocalString(oNew, "CEP_L_LIGHTSWAP", sResRef);

// This branch isn't taken, since we're only turning torches off.

/*if (nAmIOn == 1)

{

effect eLight = EffectVisualEffect(nLight);

ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLight, oNew);

} */

// free() is so much more peaceful and happy than DestroyObject(). :(

// I am concerned about the ethical implications of this. Are we freeing

// the objects by destroying them? A programmer would say yes, but we all

// know that programmers are sadistic bastards. Is destruction justified

// sometimes? If not, does the fact that entropy always increases make the

// Universe itself evil because physics demands that something, somewhere,

// is being destroyed? If so, how do we determine that destroying something

// will free it? From what are we freeing these objects by destroying them?

// Responsibility? Well, the heap, of course, and I suppose that depending

// on what it is a heap of, that would be an improvement of conditions.

// Meditate on this I shall.

DestroyObject(oTorchVictim, 0.0);

}

/* void RemoveTorches(int nRow,int nLeave);

* Remove some torches on the computer's turn.

* Type: Data Structure Deletion

* Input: int nRow, the number of the row from which to remove torches {0 to 2},

* int nLeave, the number of torches to leave in the row {0 to value-1}

*/

void RemoveTorches(int nRow,int nLeave)

{

int nColNum;

string sRowTag;

string sTorchTag;

object oMapPin;

object oTorch;

sRowTag = "0"+IntToString(nRow);

// Note that nLeave is the number of torches to leave, and this removes

// torches numbered from nLeave up to the number of torches in the row. The

// torches actually left end with the torch numbered nLeave-1, due to using

// 0-based indexing in torch labels.

for (nColNum = nLeave; nColNum <NUM_COLS; nColNum++) {

sTorchTag = "NIM_TORCH_"+sRowTag+((nColNum<10)?"0":"")+IntToString(nColNum);

// get the torch

oTorch = GetObjectByTag(sTorchTag);

// check if it's already been turned off--if it has, stop

if (!GetLocalInt(oTorch,"CEP_L_AMION")) {

break;

}

// and if it hasn't been turned off, turn it off

ToggleTorch(oTorch);

// also, turn off the map pin.

oMapPin = GetObjectByTag("WP"+sTorchTag);

SetMapPinEnabled(oMapPin,0);

}

// Speaking of more efficient and easier to read... well... ummm... at least

// this is more space efficient. Anyway, this just sets the new value of

// the row being changed in the corresponding NIM_PORTAL variable.

SetLocalInt(GetObjectByTag("NIM_PORTAL"),(nRow==0)?"nRow1Val":

((nRow==1)?"nRow2Val":"nRow3Val"),nLeave);

}

SP

* project: Game of Nim modification date: 11.05.05

* description: Game logic for the Game of Nim as implemented in a

* Neverwinter Nights module. There are a few other small scripts that

* actually use this one. They're called sp_nim_*. The sp_nim_reload script

* sets up the game area, although it makes some assumptions about the area

* geometry, but that's a story for another time.

*/

#include "zep_inc_main" // CEP stuff for ToggleTorch()

int NUM_COLS = 16; // the number of torches in a row

/* int GetNimXOR();

* Return the 3-way bitwise exclusive OR of the three row values

* Type: Calculation

* Output: int, the bitwise XOR of the three row values.

*/

int GetNimXOR()

{

int a = GetLocalInt(GetObjectByTag("NIM_PORTAL"),"nRow1Val");

int b = GetLocalInt(GetObjectByTag("NIM_PORTAL"),"nRow2Val");

int c = GetLocalInt(GetObjectByTag("NIM_PORTAL"),"nRow3Val");

return a^b^c;

}

/* int GetNimMove(int a, int b, int c);

* Figure out how many torches to keep lit in the row with value c.

* Type: Calculation

* Input: int a, the value of a safe row; int b, the value of a safe row; int c,

* the value of the row from which to subtract something.

* Output: the new value of c (the number of torches to leave in that row)

*/

int GetNimMove(int a, int b, int c)

{

int nNewC = a^b; // the number of torches to remove

if ((nNewC^(a^b)) != 0) {

SpeakString("Error in GetNimMove, XOR check = "+IntToString(nNewC^(a^b)));

} else if (nNewC <= 0) {

SpeakString("Error in GetNimMove, nNewC = "+IntToString(nNewC));

}

return nNewC;

}

// the next two functions aren't really needed.

/* string IntToBitString(int nNum);

* Convert a non-negative integer into a bit string.

* Type: Wrapper

* Input: int nNum, the integer to convert

* Output: the bit string representation of nNum, "0" if nNum is 0.

*/

string _IntToBitStringCalc(int nNum);

string IntToBitString(int nNum)

{

if (nNum == 0) {

return "0";

} else {

return _IntToBitStringCalc(nNum);

}

}

/* string IntToBitStringCalc(int nNum);

* Convert a *positive* integer into a bit string.

* Type: Calculation

* Input: int nNum, the non-negative, non-zero integer to convert

* Output: the bit string representation of nNum.

*/

string _IntToBitStringCalc(int nNum)

{

//return ( (n > 0) && (_IntToBitString(n>>>1)+IntToString(n&1)) || "0");

if (nNum > 0) {

return _IntToBitStringCalc(nNum >>> 1) + IntToString(nNum & 1);

} else {

// if this condition returns "0", the output bit string will have a

// leading 0. That's undesireable, and so is outputting an empty string

// on a non-recursed 0 input, so this function is placed in a wrapper

// that deals with 0 inputs (above). Better solutions?

// Better solution: not using the fucking thing. Argh.

return "";

}

}

/* NimTriMax(int a, int b, int c);

* Return the maximum of three integers. Equal values are handled properly.

* Type: Calculation

* Input: int a, an integer to compare; int b, an integer to compare; inc c,

* an integer to compare

* Output: the maximum of the three integers

*/

int NimTriMax(int a, int b, int c)

{

if (a > b && a > c) return a;

if (b > c) return b;

return c;

}

/* Adapted from zep_torch, Copyright (c) 2001 Bioware Corp.

* Originally modified by Dan Heidel 1/21/04 for CEP

* More modifications by Neil MacMillan 09.05.05 for Game of Nim

* I think all changes have been marked.

*/

// This could be pruned for our purposes--we don't need to turn a light on.

/* void ToggleTorch(object oTorchVictim);

* Turn a torch on if it's off, or off if it's on. This is done by replacing

* a torch that's on with one that's off and vice versa.

* Type: Replace Object

* Input: object oTorchVictim, the torch to toggle

*/

void ToggleTorch(object oTorchVictim)

{

// these were changed to refer to oTorchVictim instead of OBJECT_SELF.

location lLoc = GetLocation(oTorchVictim);

string sResRef = GetResRef(oTorchVictim);

int nAmIOn = GetLocalInt(oTorchVictim, "CEP_L_AMION");

int nLightCycle = GetLocalInt(oTorchVictim, "CEP_L_LIGHTCYCLE");

int nInitialized = GetLocalInt(oTorchVictim, "CEP_L_LIGHTINITIALIZED");

int nLightDiurnal = GetIsNight();

string sLightConst = GetLocalString(oTorchVictim, "CEP_L_LIGHTCONST");

string sLightSwap = GetLocalString(oTorchVictim, "CEP_L_LIGHTSWAP");

int nLight = ColorInit(sLightConst);

// I changed this because this way's more efficient and easier to read.

// Although now that I think about it, some math nerd might think it's

// finding the derangement of nAmIOn, but since the derangement of a set

// of size 1 is zero, well, it works out anyway since we're only switching

// from 1 to 0. Hah, suck it, math.

nAmIOn = !nAmIOn;

object oNew = CreateObject(OBJECT_TYPE_PLACEABLE, sLightSwap, lLoc);

SetLocalInt(oNew, "CEP_L_AMION", nAmIOn);

// the CEP_L_LIGHTCYCLE setting is changed, because we want the torch to be

// off all the time.

SetLocalInt(oNew, "CEP_L_LIGHTCYCLE", 0);

SetLocalInt(oNew, "CEP_L_LIGHTINITIALIZED", nInitialized);

SetLocalInt(oNew, "CEP_L_LIGHTDIURNAL", nLightDiurnal);

SetLocalString(oNew, "CEP_L_LIGHTCONST", sLightConst);

SetLocalString(oNew, "CEP_L_LIGHTSWAP", sResRef);

// This branch isn't taken, since we're only turning torches off.

/*if (nAmIOn == 1)

{

effect eLight = EffectVisualEffect(nLight);

ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLight, oNew);

} */

// free() is so much more peaceful and happy than DestroyObject(). :(

// I am concerned about the ethical implications of this. Are we freeing

// the objects by destroying them? A programmer would say yes, but we all

// know that programmers are sadistic bastards. Is destruction justified

// sometimes? If not, does the fact that entropy always increases make the

// Universe itself evil because physics demands that something, somewhere,

// is being destroyed? If so, how do we determine that destroying something

// will free it? From what are we freeing these objects by destroying them?

// Responsibility? Well, the heap, of course, and I suppose that depending

// on what it is a heap of, that would be an improvement of conditions.

// Meditate on this I shall.

DestroyObject(oTorchVictim, 0.0);

}

/* void RemoveTorches(int nRow,int nLeave);

* Remove some torches on the computer's turn.

* Type: Data Structure Deletion

* Input: int nRow, the number of the row from which to remove torches {0 to 2},

* int nLeave, the number of torches to leave in the row {0 to value-1}

*/

void RemoveTorches(int nRow,int nLeave)

{

int nColNum;

string sRowTag;

string sTorchTag;

object oMapPin;

object oTorch;

sRowTag = "0"+IntToString(nRow);

// Note that nLeave is the number of torches to leave, and this removes

// torches numbered from nLeave up to the number of torches in the row. The

// torches actually left end with the torch numbered nLeave-1, due to using

// 0-based indexing in torch labels.

for (nColNum = nLeave; nColNum <NUM_COLS; nColNum++) {

sTorchTag = "NIM_TORCH_"+sRowTag+((nColNum<10)?"0":"")+IntToString(nColNum);

// get the torch

oTorch = GetObjectByTag(sTorchTag);

// check if it's already been turned off--if it has, stop

if (!GetLocalInt(oTorch,"CEP_L_AMION")) {

break;

}

// and if it hasn't been turned off, turn it off

ToggleTorch(oTorch);

// also, turn off the map pin.

oMapPin = GetObjectByTag("WP"+sTorchTag);

SetMapPinEnabled(oMapPin,0);

}

// Speaking of more efficient and easier to read... well... ummm... at least

// this is more space efficient. Anyway, this just sets the new value of

// the row being changed in the corresponding NIM_PORTAL variable.

SetLocalInt(GetObjectByTag("NIM_PORTAL"),(nRow==0)?"nRow1Val":

((nRow==1)?"nRow2Val":"nRow3Val"),nLeave);

}

SP