C# Rpg Random Enemy Assignment - c#

I am trying to assigned a Enemy enemy class instance to a random newenemy instance.
For example:
public Enemy GetEnemy()
{
Random rand = new Random();
Enemy newenemy = new Enemy(string.Empty, 0, 0, 0, 0, 0);
if (Main.level == 1)
{
int z = rand.Next(0, 2);
if (z == 0)
{
newenemy = new Enemy("Chicken", 50, 0, 4, 11, 32);
}
else if (z == 1)
{
newenemy = new Enemy("Common Goblin", 67, 0, 8, 14, 36);
}
else if (z == 2)
{
newenemy = new Enemy("Rabbit", 50, 0, 15, 25, 9);
}
}
return newenemy;
}
Which is then used in my Battle function: (I'll post some cause it is somewhat long)
if (whathappen == 2 || whathappen == 4 || whathappen == 6 || whathappen == 8 || whathappen == 10) //battle code
{
Console.Write("You were engaged by the enemy! \nYou are fighting an enemy {0}\n", GetEnemy().name);
while (Main.health > 0 || GetEnemy().health > 0)
{
Console.Write("1) Attack With Weapon ");
int choose;
int.TryParse(Console.ReadLine(), out choose);
if (choose == 1)
{
int miss = r.Next(0, 6);
if (miss < 6)
{
int totaldamage = (r.Next(0, Main.RHand.damage) + r.Next(0, Main.LHand.damage)) - GetEnemy().armor;
GetEnemy().health -= totaldamage;
Console.WriteLine("You attack the {0} with a {1} and deal {2} damage!", GetEnemy().name, Main.RHand.name, totaldamage);
Console.WriteLine("The {0} has {1} health remaning!", GetEnemy().name, GetEnemy().health);
However, when I am testing my game, no enemy is assigned during the battle and I don't understand why. This is the output:
You were engaged by the enemy!
You are fighting an enemy! (The enemy name should come after enemy)
You attack the (name should be here again) with a Bronze Dagger and deal 15 damage!
Can anyone explain why this is happeneing?

There's a few problems with your code:
Based on your description of your problem, my guess is that Main.level is not equal to 1
You're not quite using Random.Next correctly: Random.Next(0, 2) will never equal 2
But even after you fix those problems, you have a bigger issue that you're creating way too many instances of your Enemy and you aren't saving them anywhere.
Every time you call your GetEnemy() method you're making a brand new instance of your class, accessing one property on it, then it's getting discarded. You are attempting to maintain the state of your enemy, you will need to save the instance you create and reuse it, e.g.:
Enemy enemy = GetEnemy();
while (Main.health > 0 || enemy.health > 0)
{
.
.
.
}

Related

Incrementation in loop or if statement

So here's the thing. I have a game in which I have a spawner - to spawn obstacles. And I want after every 5th obstacle to increase the speed. Example: 1st, 2nd, 3rd, 4th, 5th obstacles have default speed = 5 and I want 6th, 7th, 8th, 9th, 10th to have an increased speed to speed = 10 and etc. I already have some nested if statements,but I want to inscrease the speed infitely for example to have speed = 1000 for the 100th obstacle. Here's my code:
IEnumerator SpawnObstacles()
{
int i = 0;
do
{
var randomTime = Random.Range(minTime, maxTime);
var randomObstacle = Random.Range(0, obstacles.Length);
yield return new WaitForSeconds(randomTime);
var obstacle = Instantiate(obstacles[randomObstacle], transform.position, Quaternion.identity);
Destroy(obstacle, 10f);
i++;
if(i >= 10)
{
IncreaseSpeed();
if(i >= 20)
{
IncreaseSpeed();
if(i >= 30)
{
IncreaseSpeed();
}
}
}
}
while (true);
}
private void IncreaseSpeed()
{
Obstacle obs = FindObjectOfType<Obstacle>();
obs.speed += 5f;
}
The modulo operator % helps with things like this. It gives the remainder of the division operation, for example:
3%5 is 3
4%5 is 4
5%5 is 0
6%5 is 1
7%5 is 2
8%5 is 3
9%5 is 4
10%5 is 0
So if your x is starting at 0, and you want to increase speed every 5, you just look at whenever the modulo is 4..
x can go as high as you like (well, int.MaxValue) but the modulo will always be 0..4:
//do some stuff
//should we increase speed?
if(x % 5 == 4)
IncreaseSpeed();
Handy tool for making circular arrays too.. someArray[i % someArray.Length] will never go out of bounds, because the result of a modulo is always between 0 and one less than the length
What you could do instead is either :
Keep track of how many obstacle instances you've spawned so far using a variable(maybe a static variable https://www.c-sharpcorner.com/UploadFile/1ce252/static-variables-and-static-methods-in-C-Sharp/ ) and make sure to simply increase the speed for every 5 instances using modulo %. You can take a look for more information here : https://blog.mattclemente.com/2019/07/12/modulus-operator-modulo-operation.html
A quick introduction to Modulos:
//It essentially shows the rest of a division
5 / 5 == 1
//Since there's no rest there,
5 % 5 == 0
//But in the case of where there is a rest
6 / 5 == 1 (Rest 1)
//Hence
6 % 5 == 1
//Other examples :
7 % 5 == 2
8 % 5 == 3
9 % 5 == 4
10 % 5 == 0 //!! The result is back to zero here as 10/5 == 2 with no Rest
Implement a recursive function that's in the Object that spawns in another object with the same attributes but pass on a parameter which indicates how many instances have been spawned so far and check if it's the 5th instance, if so add speed to it.
You have a Coroutine in an infinite loop anyway ... why not simply do
IEnumerator SpawnObstacles()
{
do
{
for(var x = 0; x < 5; x++)
{
var randomTime = Random.Range(minTime, maxTime);
var randomObstacle = Random.Range(0, obstacles.Length);
yield return new WaitForSeconds(randomTime);
var obstacle = Instantiate(obstacles[randomObstacle], transform.position, Quaternion.identity);
Destroy(obstacle, 10f);
}
// These lines are automatically reached after every 5 iterations
// no need for any módulo or other magic check
Obstacle obs = FindObjectOfType<Obstacle>();
obs.speed += 5f;
}
while (true);
}
Or since I'm not sure of this entire FindObjectWithTag thingy ... actually it seems to me it is the same object you Instantiate right? So why not simply
IEnumerator SpawnObstacles()
{
var speed = 5;
do
{
for(var x = 0; x < 5; x++)
{
var randomTime = Random.Range(minTime, maxTime);
var randomObstacle = Random.Range(0, obstacles.Length);
yield return new WaitForSeconds(randomTime);
var obstacle = Instantiate(obstacles[randomObstacle], transform.position, Quaternion.identity);
obstacle.GetComponent<Obstacle>().speed = speed;
Destroy(obstacle, 10f);
}
// These lines are automatically reached after every 5 iterations
// no need for any módulo or other magic check
speed += 5f;
}
while (true);
}
Though to me it stays a bit unclear how exactly your speed shall grow. From your description it sounds like you want to add 5 after every group of iterations .. however this would mean you have speed = 100 (20*5) for the 100th object .. not speed = 1000 as you wrote
The simplest thing i can think of is setting a counter for the spawned obstacles and incrementing the speed after it reaches 5 and resetting the counter to zero. You don't really need to get into the fancy stuff for something so simple just use an if statement to check the counter.Hope I helped, cheers!

dice roll game stuck in a loop

I am trying to make a program for a game of pig where there user enters a point total to play for and then takes turns with a computer player until one player reaches the point total. For the human turn the player rolls and if they roll 2-6 then they can use r to roll again or h to hold. when hold is selected it adds up the total points and goes to the next turn. If 1 is rolled then they get 0 points for the round and it goes to the computers turn. For some reason when I select h to hold it just keeps going and when it does end the points don't get added up. Not sure how to fix this
any help would be appericated
static int pigRoll()
{
Random random = new Random();
int die1 = 0;
die1 = random.Next(1, 6);
Console.WriteLine($"You rolled {die1}");
return die1;
}
static double humanTurn()
{
double pointTotal = 0;
string gameSelect = null;
var pigDiceRoll = 0;
Console.WriteLine("It's your turn");
do
{
pigDiceRoll = pigRoll();
if (pigDiceRoll != 1)
{
Console.WriteLine("r to roll or h to hold (r/h)");
gameSelect = Console.ReadLine();
pointTotal = pointTotal + pigDiceRoll;
}
else if(pigDiceRoll ==1)
{
pointTotal = 0;
}
} while (gameSelect != "r" || pigDiceRoll != 1);
Console.WriteLine($"Your turn point total is {pointTotal}");
return pointTotal;
}
The while statement should read:
while (gameSelect == "r" && pigDiceRoll != 1)
This translates to keep looping while the user wants to roll again and they didn't roll a 1.
Alternatively you could use:
while (!(gameSelect != "r" || pigDiceRoll == 1))
Both are logically the same, but the first is probably easier to read.

C# craps simulation

I have seen questions related to Craps, but they were related to python and Java. I hope someone familiar with C# can help me. The code below is meant to simulate the game of craps. The code mostly works, except for one issue.
I suppose I should briefly explain the game. Two 6 sided dice are rolled. If the result is a 7 or 11. The player or "shooter" wins automatically. If a 2, 3, or 12 is rolled, the shooter loses automatically. However, if another number is rolled, that number becomes the "point". The shooter rolls again until either they roll the point again or a 7. If the point is rerolled the shooter wins. If a 7 is rolled, this time it is a loss.
So, the problem is that this code still gives wins or losses automatically for 2, 3, 11, and 12 after the first roll. For example: The shooter rolls a 6. 6 becomes the point and the shooter rolls again. If the shooter rolls a 3 this code marks this result as a loss when it should just keep rolling until a 6 or 7 is rolled. This incorrect end happens whenever a 2, 3, 11, or 12 is rolled after the first roll. Any help on correcting the logic of this code would be greatly appreciated. Thanks Also, I hope this question is formatted correctly. Please let me know if it is not.
class Craps
{
const int dieSides = 6;
int roll;
//const int repeatGame = 1000;
Random random = new Random();
public void RollDice()
{
int die1 = 0;
int die2 = 0;
die1 = random.Next(1, 7);
die2 = random.Next(1, 7);
roll = die1 + die2;
//Console.WriteLine("The shooter roled: {0}", roll);
}
public void PlayCraps()
{
RollDice();
Console.WriteLine("The shooter roled: {0}", roll);
int gameStatus = 0;
int point = roll;
int numRolls = 1;
if (roll != 7 || roll != 11 || roll != 2 || roll != 3 || roll != 12)
{
Console.WriteLine("The point is: {0}", point);
}
while (gameStatus < 1)
{
if (roll == 7 || roll == 11)
{
Console.WriteLine("You won!");
gameStatus++;
break;
}
else if (roll == 2 || roll == 3 || roll == 12)
{
Console.WriteLine("You lost.");
gameStatus++;
break;
}
else
{
while (point != roll || roll != 7)
{
RollDice();
if (roll == point)
{
Console.WriteLine("The shooter roled: {0}", roll);
Console.WriteLine("You won!");
numRolls++;
gameStatus++;
break;
}
if (roll == 7)
{
Console.WriteLine("The shooter roled: {0}", roll);
Console.WriteLine("You lost");
numRolls++;
gameStatus++;
break;
}
else
{
numRolls++;
Console.WriteLine("The shooter roled: {0}", roll);
break;
}
}
}
}
Console.WriteLine("This game was {0} roll(s)", numRolls);
}
static void Main(string[] args)
{
Craps NewGame = new Craps();
NewGame.PlayCraps();
Console.ReadLine();
}
}
User pm100 commented saying that the final break returned the code back to the outer loop and that this was causing the error. Removing this fixed the issue described in my question. There may be other errors or ways to improve this code, but that comment solved the particular focus of this question.

Random sprite in Unity

void Start() {
rch = Random.Range(0, 20);
ball = GameObject.FindGameObjectWithTag("Ball");
if (rch % 2 == 0) {
ball.GetComponent<SpriteRenderer>().sprite = s1;
}
else
{
ball.GetComponent<SpriteRenderer>().sprite = s2;
}
Debug.Log(rch);
}
I have the following code which is supposed to select a number between 0 and 20 and if the number is divisible with 2 spawn one spite and otherwise the other.
The code is called every time the object is destroyed and that's why I use the start function. With Debug.Log(rch), I print the random number and even if the number is not divisible by 2, I only get the ball1.

Chess negamax algorithm moves pieces back and forth. What's wrong?

Okay, so I'll admit up front this one's going to be a bit long. I'm writing a chess engine for C#, with the eventual goal including UCI implementation. I've got it to the point where, given a board, the engine will generate a list of all valid moves; however, my evaluation code seems to be struggling, because when playing a game against itself, the engine will move two pawns on either side, and then just move a piece back and forth on either side. I'm going to outline the crucial parts of the program below in order to best allow you to understand under what conditions my code is called and used, in the hope that it will help you answer my question.
This is just the main method called by my interface, nothing exciting here.
class BreezeEngine
{
// Declares Piece Values
public const int pawn = 1, knight = 4, bishop = 6, rook = 8, queen = 16, king = 60;
public const int toDepth = 4;
public static void BoardGen(string mode)
{
Board chessBoard = new Board(new int[8, 8] {
{ 8, 4, 6,16,60, 6, 4, 8 },
{ 1, 1, 1, 1, 1, 1, 1, 1 },
{ 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0 },
{-1,-1,-1,-1,-1,-1,-1,-1 },
{-8,-4,-6,-16,-60,-6,-4,-8 }
}, 0, true, true, true);
PlayGame(chessBoard, true);
return;
}
This method is working just fine.
It returns a list of moves with format
x1, y1, x2, y2, weight
The weight this method generates is the value of whatever piece is killed. If you have questions, let me know.
private static List<int[]> CalcFutures(Board chessBoard)
{
// Move generation stuff.
}
This method isn't complete yet (since it doesn't handle castling or en passant), but it's basically just to generate a new board object from any given move.
private static Board MoveToBoard(int[] move, Board board)
{
int[,] newBoard = new int[8, 8];
Array.Copy(board.Pieces(), newBoard, 64);
newBoard[move[3], move[2]] = newBoard[move[1], move[0]];
newBoard[move[1], move[0]] = 0;
if (newBoard[move[3], move[2]] == pawn && move[3] == 7) newBoard[move[3], move[2]] = queen;
if (newBoard[move[3], move[2]] == -pawn && move[3] == 0) newBoard[move[3], move[2]] = -queen;
return new Board(newBoard, board.Depth() + 1, !board.IsTurn(), true, true);
}
This code is probably not needed, but I'm including it on the off chance that a typo in here is causing the bug. This is just a very basic user interface to allow me to play a game against my engine, or have the engine play itself.
private static void PlayGame(Board chessBoard, bool demo)
{
int[] move = new int[5];
if (!(chessBoard.IsTurn() || demo))
{
Console.WriteLine("Type in your move one integer at a time: x1,y1,x2,y2");
move[0] = Convert.ToInt32(Console.ReadLine());
move[1] = Convert.ToInt32(Console.ReadLine());
move[2] = Convert.ToInt32(Console.ReadLine());
move[3] = Convert.ToInt32(Console.ReadLine());
}
else
{
Console.WriteLine("Calculating Move..." + chessBoard.IsTurn());
move = Evaluate(CalcFutures(chessBoard), chessBoard);
}
if (Math.Abs(chessBoard.Pieces()[move[3], move[2]]) == king)
{
if (chessBoard.IsTurn()) Console.Write("White Wins");
else Console.Write("Black Wins");
return;
}
chessBoard = MoveToBoard(move, chessBoard);
chessBoard.SetDepth(0);
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
Console.Write(chessBoard.Pieces()[i, j].ToString().PadLeft(3, ' '));
}
Console.WriteLine();
}
PlayGame(chessBoard, demo);
}
}
Now, I'm going on a brief tangent before presenting the evaluation algorithm itself. This is the board object you've seen referenced many other times throughout the code. It contains an array for the chess board, as well as other variables necessary for defining the game's current state.
class Board
{
bool isTurn;
bool castling;
bool enemyCastling;
int[,] pieces = new int[8, 8];
int weight = 0;
int depth;
public Board(int[,] inBoard, int inDepth, bool inIsTurn, bool inCastling, bool inEnemyCastling)
{
Array.Copy(inBoard, pieces, 64);
isTurn = inIsTurn;
castling = inCastling;
enemyCastling = inEnemyCastling;
depth = inDepth;
}
public int Weight()
{
int sum = 0;
foreach (int i in pieces)
sum += i;
weight = sum;
return weight;
}
public int[,] Pieces() { return pieces; }
public bool IsTurn() { return isTurn; }
public void ToggleTurn() { isTurn = !isTurn; return; }
public int Depth() { return depth; }
public void SetDepth(int inDepth)
{
depth = inDepth;
}
}
Now that I've outlined the rest of my program, here is the evaluation method itself. The code takes in a list of moves, and if it is to the furthest depth that is supposed to be searched, it simply returns the one with the greatest absolute value. If it's not to the bottom, it simply generates a list of futures, for each move in the list of futures it receives, and calls itself again. The returned value is then added to the original move's weight, and compared to the best move it has found so far. However, I've been having issues with this approach, and I'm guessing it's either because I've misunderstood how negamax is supposed to work, or I've made a typo somewhere along the way. Any idea what's going on?
private static int[] Evaluate(List<int[]> futures, Board chessBoard)
{
int[] bestMove = new int[5];
bestMove[0] = 30;
if (chessBoard.Depth() >= toDepth)
{
foreach (int[] move in futures)
{
if (Math.Abs(move[4]) > Math.Abs(bestMove[4]))
{
Array.Copy(move, bestMove, 5);
}
}
}
else
{
foreach (int[] move in futures)
{
Board newBoard = MoveToBoard(move, chessBoard);
int[] testMove = Evaluate(CalcFutures(newBoard), newBoard);
move[4] += testMove[4];
if (bestMove[0] == 30) bestMove = move;
if (chessBoard.IsTurn())
{
if (move[4] > bestMove[4])
{
Array.Copy(move, bestMove, 5);
}
}
else
{
if (move[4] < bestMove[4])
{
Array.Copy(move, bestMove, 5);
}
}
}
}
return bestMove;
}
You're doing the evaluation wrong. You have to make sure that either side is picking its own best future. Honestly, minimax is easy to implement given your data structure for moves. Here's the fixed evaluation function.
public static int[] Evaluate(List<int[]> futures, Board chessBoard)
{
int[] bestMove = new int[5];
Random rndMove = new Random();
Array.Copy(futures[rndMove.Next(futures.Count)], bestMove, 5);
if (chessBoard.Depth() == toDepth)
{
if (chessBoard.IsTurn())
{
// Maximum
//bestMove[4] = -1000000;
foreach (int[] move in futures)
{
if (move[4] > bestMove[4])
{
Array.Copy(move, bestMove, 5);
}
}
}
else
{
// Minimum
//bestMove[4] = 1000000;
foreach (int[] move in futures)
{
if (move[4] < bestMove[4])
{
Array.Copy(move, bestMove, 5);
}
}
}
}
else
{
if (chessBoard.IsTurn())
{
// Maximum
//bestMove[4] = -1000000;
foreach (int[] move in futures)
{
if (Math.Abs(chessBoard.Pieces()[move[3], move[2]]) == king) return move;
Board newBoard = MoveToBoard(move, chessBoard);
move[4] += Evaluate(CalcFutures(newBoard), newBoard)[4];
if (move[4] > bestMove[4])
{
Array.Copy(move, bestMove, 5);
}
}
}
else
{
// Minimum
//bestMove[4] = 1000000;
foreach (int[] move in futures)
{
if (Math.Abs(chessBoard.Pieces()[move[3], move[2]]) == king) return move;
Board newBoard = MoveToBoard(move, chessBoard);
move[4] += Evaluate(CalcFutures(newBoard), newBoard)[4];
if (move[4] < bestMove[4])
{
Array.Copy(move, bestMove, 5);
}
}
}
}
//Console.WriteLine(bestMove[4]);
return bestMove;
}

Categories

Resources