C# multidimensional array value changes unexpectedly - c#

Hello i have a realy strange behavior in this piece of code:
public class IGraphics
{
public int[,] screen;
private int[,] world;
private int[,] entitys;
private int[,] buffer;
private int screenW;
private int screenH;
public IGraphics(int screenW, int screenH) {
this.screenH = screenH;
this.screenW = screenW;
screen = new int[screenW + 1, screenH];
buffer = new int[screenW + 1, screenH];
}
public void loadWorld(int[,] world) {
this.world = world;
}
public void clear() {
screen = new int[screenW + 1, screenH];
world = new int[screenW, screenH];
for (int y = 0; y < world.GetLength(1); y++) {
for (int x = 0; x < world.GetLength(0); x++) {
world[x, y] = 0;
}
}
}
private void loadScreen() {
}
private void updateEntitys()
{
entitys = new int[screenW, screenH];
List<GameObject> EntRow = Common.world.getEntitys();
for (int i = 0; i < EntRow.Count(); i++)
{
entitys[EntRow[i].x, EntRow[i].y] = EntRow[i].Icon;
}
}
public void draw() {
updateEntitys();
for (int y = 0; y < screen.GetLength(1); y++)
{
for (int x = 0; x < screen.GetLength(0) - 1; x++)
{
if (entitys[x, y] == 0)
{
screen[x, y] = world[x, y];
}
else
{
screen[x, y] = entitys[x, y];
}
}
screen[screen.GetLength(0) - 1, y] = 123;
}
if (buffer.Cast<int>().SequenceEqual(screen.Cast<int>()))
{
return;
}
Console.Clear();
buffer = screen;
for (int y = 0; y < screen.GetLength(1); y++) {
for (int x = 0; x < screen.GetLength(0); x++) {
if (screen[x, y] == 123)
{
Console.WriteLine();
}
else {
Console.Write(objectStore.getIcon(screen[x, y]));
}
}
}
}
}
the problem comes in the Draw() function where i set the value of the screen[,] array for some reason it also change the value of the buffer[,] array before the control also tried moving the buffer[,] in a seperate class but i had the same problem.
Someone as an explanation?

When you assign a reference variable to another variable, you copy the pointer to that variable, instead of copying the content, so what you end up with is two variables that point to the same array.
Try to instead copy the array using Clone or copy or something. I think its screen.CopyTo
screen.CopyTo(buffer, 0);

If you look at the body of the method called draw, you will notice this assignment:
buffer = screen;
This is might the cause of the change you noticed.

See the other answers for possible reasons for your problem. However, the following code shows that changing screen[] does not change buffer[]. This kind of effort on your part would allow you to focus your investigation elsewhere. The principle is to first simply.
int counter = 1;
public void draw()
{
for (int y = 0; y < screen.GetLength(1); y++)
{
for (int x = 0; x < screen.GetLength(0) - 1; x++)
{
screen[x, y] = counter++;
}
screen[screen.GetLength(0) - 1, y] = 123;
}
if (buffer.Cast<int>().SequenceEqual(screen.Cast<int>()))
{
MessageBox.Show("Help!");
return;
}
// check again
for (int y = 0; y < screen.GetLength(1); y++)
{
for (int x = 0; x < screen.GetLength(0) - 1; x++)
{
if (screen[x, y] == buffer[x, y])
{
MessageBox.Show("Help two!");
return;
}
}
}

Related

Method returns an error "not all code paths return a value, please assist

Using unity to create a procedural map I'm getting the Error "Cs0161: not all code paths return a value" , I'm still pretty new to coding and I may have made some mistake.
I've tried google but the answers don't even make sense to me
yet
MapData GenerateMapData()
{
float[,] noiseMap = Noise.GenerateNoiseMap(mapChunkSize, mapChunkSize, seed, noiseScale, octaves, lacunarity, persistance, offset);
Color[] colourMap = new Color[mapChunkSize * mapChunkSize];
for (int y = 0; y < mapChunkSize; y++)
{
for (int x = 0; x < mapChunkSize; x++)
{
float currentHeight = noiseMap[x, y];
for (int i = 0; i < regions.Length; i++)
{
if (currentHeight <= regions[i].height)
{
colourMap[y * mapChunkSize + x] = regions[i].colour;
break;
}
}
}
return new MapData(noiseMap, colourMap);
}
}
Error CS0161 'MapGenerator.GenerateMapData()': not all code paths return a value Assembly-CSharp
After the for loops you should also return a value. See the code below and the comment at the bottom of the method:
MapData GenerateMapData()
{
float[,] noiseMap = Noise.GenerateNoiseMap(mapChunkSize, mapChunkSize, seed, noiseScale, octaves, lacunarity, persistance, offset);
Color[] colourMap = new Color[mapChunkSize * mapChunkSize];
for (int y = 0; y < mapChunkSize; y++)
{
for (int x = 0; x < mapChunkSize; x++)
{
float currentHeight = noiseMap[x, y];
for (int i = 0; i < regions.Length; i++)
{
if (currentHeight <= regions[i].height)
{
colourMap[y * mapChunkSize + x] = regions[i].colour;
break;
}
}
}
return new MapData(noiseMap, colourMap);
}
// You should also return something here
}
It is theoretically possible that mapChunkSize can have a value of 0 (zero) and it will not enter the first for loop at all. That's why you need a return statement at the bottom as well.
The reason why you are receiving that error is because your return statement is inside the for loops. More specifically, it can happen that neither of the loops execute (e.g. mapChunkSize == 0. Then 0 isn't less than 0 and the program doesn't enter the for loops) and that thus nothing gets returned. So either move the return outside the loops or add another return outside the loops.
MapData GenerateMapData()
{
float[,] noiseMap = Noise.GenerateNoiseMap(mapChunkSize, mapChunkSize, seed, noiseScale, octaves, lacunarity, persistance, offset);
Color[] colourMap = new Color[mapChunkSize * mapChunkSize];
for (int y = 0; y < mapChunkSize; y++)
{
for (int x = 0; x < mapChunkSize; x++)
{
float currentHeight = noiseMap[x, y];
for (int i = 0; i < regions.Length; i++)
{
if (currentHeight <= regions[i].height)
{
colourMap[y * mapChunkSize + x] = regions[i].colour;
break;
}
}
}
/*return new MapData(noiseMap, colourMap); //uncommenting this might still leave you with the functionality you want */
}
return new MapData(noiseMap, colourMap);
}
you are not returning any thing outside the first loop, try to evaluate values inside the loops and then return from outside the loop.
for (int y = 0; y < mapChunkSize; y++)
{
for (int x = 0; x < mapChunkSize; x++)
{
float currentHeight = noiseMap[x, y];
for (int i = 0; i < regions.Length; i++)
{
if (currentHeight <= regions[i].height)
{
colourMap[y * mapChunkSize + x] = regions[i].colour;
break;
}
}
}
return new MapData(noiseMap, colourMap);
}
add return statement here

Efficient way to Flip a 2D Array Horizontally or Vertically

The problem is to make a program the flips a 2D (2 by 2) array either horizontally(H) or vertically(V), depending on the input the user puts. For example, if the user puts "HHHVVHVHV", it means to flip 5 times horizontally, and 4 times vertically. The code I attached below works, but I am not sure if there is a built-in method in C# to flip arrays or an easier way to do it.
using System;
using System.Linq;
namespace _2019JuniorQ4
{
class Program
{
static void Main(string[] args)
{
//Varables
int[,] grid = new int[2, 2] { { 1, 2 }, { 3, 4 } };
int[,] flippedGrid = new int[2, 2];
string input = "";
int h = 0;
int v = 0;
int flipHorizontal = 0;
int flipVertical = 0;
//Input
input = Console.ReadLine();
//Count number of times
for (int i=0; i<input.Length; i++)
{
if (input[i] == Convert.ToChar("H"))
{
h++;
}
else if(input[i] == Convert.ToChar("V"))
{
v++;
}
}
//Process
flipHorizontal = h % 2;
flipVertical = v % 2;
//Vertical flip
if (flipVertical == 1)
{
for (int y = 0; y < 2; y++)
{
for (int x = 1; x >= 0; x--)
{
flippedGrid[y, Math.Abs(x - 1)] = grid[y,x];
}
}
grid = flippedGrid.Clone() as int[,];
}
//Horizontal Flip
if (flipHorizontal == 1)
{
for (int x = 0; x < 2; x++)
{
for (int y = 1; y >= 0; y--)
{
flippedGrid[Math.Abs(y - 1), x] = grid[y,x];
}
}
grid = flippedGrid.Clone() as int[,];
}
//Output
for (int x=0; x<2; x++)
{
for (int y=0; y<2; y++)
{
Console.Write(grid[x,y]+" ");
}
Console.WriteLine();
}
}
}
}

How to generate a key for a procedurally generated map using diamond-square algorithm?

I don't know if key is the right word for this but I implemented a version of the Diamond-Square algorithm and I was wondering if there is any way to "save" the current output and recreate it using a simple (6 to 10 characters) key. The only way I tought so far is to save it as a single number/string containing the output value of each cell but for a 17X17 grid I would have at least a 289 characters long sequence (assuming the output is a single digit per cell).
Since the algorithm is random I can't think of a better way to do it. If it is impossible can you tell me about better algorithm to use. Thank you :)
PS: I'm using unity 3D and C#
the following class is from the unity tutorial for procedural cave generation, but ive included it because it includes the core elements you need. it uses a seed to generate a map, the same seed will regenerate the same map.... you could use a seed in your generation process, even if it is random, then you could always reuse that seed to return to that map.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class MapGenerator : MonoBehaviour {
public int width;
public int height;
public string seed;
public bool useRandomSeed;
[Range(0,100)]
public int randomFillPercent;
int[,] map;
private void Start()
{
GenerateMap();
}
void GenerateMap()
{
map = new int[width, height];
RandomFillMap();
for (int i = 0; i < 5; i++)
{
SmoothMap();
}
}
void RandomFillMap()
{
if (useRandomSeed)
{
seed = Time.time.ToString();
}
System.Random prng = new System.Random(seed.GetHashCode());
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
if (x == 0 || x == width - 1 || y == 0 || y == height - 1)
{
map[x, y] = 1;
}
else
{
map[x, y] = (prng.Next(0, 100) < randomFillPercent) ? 1 : 0;
}
}
}
}
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
GenerateMap();
}
}
void SmoothMap()
{
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
int neighborWallTiles = GetSurroundingWallCount(x, y);
if (neighborWallTiles > 4)
{
map[x, y] = 1;
}
else if (neighborWallTiles<4)
{
map[x, y] = 0;
}
}
}
}
int GetSurroundingWallCount(int gridx, int gridy)
{
int wallcount = 0;
for(int neighborx=gridx-1; neighborx<=gridx + 1; neighborx++)
{
for (int neighbory = gridy - 1; neighbory <= gridy + 1; neighbory++)
{
if (neighborx >= 0 && neighborx < width && neighbory >= 0 && neighbory < height)
{
if (neighborx != gridx || neighbory != gridy)
{
wallcount += map[neighborx, neighbory];
}
}
else
{
wallcount++;
}
}
}
return wallcount;
}
void OnDrawGizmos()
{
if (map != null)
{
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
Gizmos.color = (map[x, y] == 1) ? Color.black : Color.white;
Vector3 pos = new Vector3(-width / 2 + x + .5f, 0, -height / 2 + y + .5f);
Gizmos.DrawCube(pos, Vector3.one);
}
}
}
}
}
I did like the comments said I used Random.InitState provided with unity and everything is working fine. Thanks Again!
UnityEngine.Random.InitState(seed);
I can control the seed as I wish and it gives me the same map for the same seed.

OnPaint called only once in C#

I'd like to make a Cellular Automaton in C#. This is a fairly easy task, which I almost have no problem with. However I'm getting an awful performance by just drawing rectangles of size one in Windows Forms Application, so my question is:
How to draw all the pixels one by one and yet have a good performance?
EDIT:
Okay, so this is my code:
public partial class Form1 : Form
{
Task t;
const int _numberOfStates = 5;
const int _resolutionX = 1920, _resolutionY = 1200;
Color[] states = new Color[_numberOfStates] { Color.Aqua, Color.Green, Color.Orange, Color.Red, Color.Blue };
Bitmap bmp = new Bitmap(1920, 1200);
short[,] map = new short[_resolutionX, _resolutionY];
public Form1()
{
InitializeComponent();
Size = new Size(_resolutionX, _resolutionY);
Random rand = new Random();
for (int x = 0; x < _resolutionX; x++)
for (int y = 0; y < _resolutionY; y++)
map[x, y] = (short)rand.Next(_numberOfStates);
t = new Task(() => { MapToBitmap(); IterateMap(); });
t.Start();
}
protected override void OnPaint(PaintEventArgs e)
{
t.Wait();
e.Graphics.DrawImage(bmp, new PointF(0, 0));
t = new Task(() => { MapToBitmap(); IterateMap(); });
t.Start();
}
void MapToBitmap()
{
for (int x = 0; x < _resolutionX; x++)
for (int y = 0; y < _resolutionY; y++)
bmp.SetPixel(x, y, states[map[x, y]]);
}
void IterateMap()
{
for (int x = 0; x < _resolutionX; x++)
for (int y = 0; y < _resolutionY; y++)
for (int i = -1; i <= 1; i++)
for (int j = -1; j <= 1; j++)
if ((i != 0 || j != 0) && x + i >= 0 && x + i <= _resolutionX && y + j >= 0 && y + j <= _resolutionY)
map[x, y] = (short)((map[x, y] + 1) % _numberOfStates);
}
}
Don't bother looking at IterateMap() and MapToBitmap() functions, however my problem now is, the OnPaint function is only called once, so I get only one iteration.
Any idea why?

C# Array isn't holding data [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I'm trying to create a dungeon generator for a project I've been working on based off of this algorithm. I've gotten everything down, but my array (Fig. 1) doesn't seem to be holding giving the map data for some reason. I'm using three types of data to determine if a cell in the map is either empty (0), a space a character can be on (1), a hallway (2), or a wall (3).
I've gotten a bit stuck on this portion so any help is appreciated!
EDIT: The problem is the map object isn't storing the data in the loop shown in Fig. 1. Sorry for being so vague.
(Fig. 1)
for (int i = 0; i < roomList.Count; i++)
{
for (int x = roomList[i].X; x < (roomList[i].X + roomList[i].W); x++)
{
for (int y = roomList[i].Y; y < (roomList[i].Y + roomList[i].H); y++)
{
map[x, y] = 1;
}
}
}
(All of my relevant code)
namespace Project
{
}
public class Room
{
int xValue, yValue, widthValue, heightValue;
public int X
{
get { return xValue; }
set { xValue = value; }
}
public int Y
{
get { return yValue; }
set { yValue = value; }
}
public int W
{
get { return widthValue; }
set { widthValue = value; }
}
public int H
{
get { return heightValue; }
set { heightValue = value; }
}
}
public class DungeonGenerate
{
public int baseWidth = 513;
public int baseHeight = 513;
public int width = 64;
public int height = 64;
Color[,] arrayColor;
Random rand = new Random();
Room room = new Room();
Rectangle[,] rectMap;
public void Generate()
{
rectMap = new Rectangle[baseWidth, baseHeight];
//Creates a 2-D Array/Grid for the Dungeon
int[,] map = new int[baseWidth, baseHeight];
//Determines all the cells to be empty until otherwise stated
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
map[x, y] = 0;
}
}
//Determines the amount of rooms in the dungeon
int minRooms = (width * height) / 300;
int maxRooms = (width * height) / 150;
int amountOfRooms = rand.Next(minRooms, maxRooms);
//Room dimensions
int widthRoot = Convert.ToInt32(Math.Round(Math.Sqrt(width * 2)));
int heightRoot = Convert.ToInt32(Math.Round(Math.Sqrt(height * 2)));
int minWidth = Convert.ToInt32(Math.Round((width * .5) / widthRoot));
int maxWidth = Convert.ToInt32((width * 2) / widthRoot);
int minHeight = Convert.ToInt32(Math.Round(height * .5) / heightRoot);
int maxHeight = Convert.ToInt32((height * 2) / heightRoot);
//Creates the rooms
List<Room> roomList = new List<Room>(amountOfRooms);
for (int i = 0; i < amountOfRooms; i++)
{
bool ok = false;
do
{
room.X = rand.Next(width);
room.Y = rand.Next(height);
room.W = (rand.Next(maxWidth)) + minWidth;
room.H = (rand.Next(maxHeight)) + minHeight;
if (room.X + room.W >= width && room.Y + room.H >= height)
{
continue;
}
for (int q = 0; q < roomList.Count; q++)
{
if (room.X > roomList[q].X && room.X < roomList[q].X + room.W && room.Y > roomList[q].Y && room.Y < roomList[q].Y + room.H)
{
ok = false;
break;
}
}
ok = true;
roomList.Add(room);
} while (!ok);
}
//This will create hallways that lead to and from the rooms
int connectionCount = roomList.Count;
List<Point> connectedCells = new List<Point>((width * height));
for (int i = 0; i < connectionCount; i++)
{
Room roomA = roomList[i];
int roomNum = i;
while (roomNum == i)
{
roomNum = rand.Next(roomList.Count);
}
Room roomB = roomList[roomNum];
//Increasing this will make the hallway more straight, decreasing it will make the hallway more skewed
int sidestepChance = 10;
Point pointA = new Point(x: (rand.Next(roomA.W)) + roomA.X, y: (rand.Next(roomA.H)) + roomA.Y);
Point pointB = new Point(x: (rand.Next(roomB.W)) + roomB.X, y: (rand.Next(roomB.H)) + roomB.Y);
while (pointA != pointB)
{
int num = rand.Next() * 100;
if (num < sidestepChance)
{
if (pointB.X != pointA.X)
{
if (pointB.X > pointA.X)
{
pointB.X--;
}
else
{
pointB.X++;
}
}
}
else if(pointB.Y != pointA.Y)
{
if (pointB.Y > pointA.Y)
{
pointB.Y--;
}
else
{
pointB.Y++;
}
}
}
if (pointB.X < width && pointB.Y < height)
{
connectedCells.Add(pointB);
}
}
//Fills the room with data
for (int i = 0; i < roomList.Count; i++)
{
for (int x = roomList[i].X; x < (roomList[i].X + roomList[i].W); x++)
{
for (int y = roomList[i].Y; y < (roomList[i].Y + roomList[i].H); y++)
{
map[x, y] = 1;
}
}
}
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
if (map[x, y] == 0)
{
bool wall = false;
for (int yy = y - 2; yy < y + 2; yy++)
{
for (int xx = x - 2; xx < x + 2; xx++)
{
if (xx > 0 && yy > 0 && xx < width && yy < height)
{
if (map[xx, yy] == 1 || map[xx, yy] == 2)
{
map[x, y] = 3;
wall = true;
}
}
}
if (wall)
{
break;
}
}
}
}
}
//Rendering the Map and giving it some Color (Sort of)!
int scaler = baseWidth / width;
for (int x = 0; x < baseWidth; x++)
{
for (int y = 0; y < baseHeight; y++)
{
rectMap[x, y] = new Rectangle(x, y, 1, 1);
arrayColor = new Color[baseWidth, baseHeight];
switch (map[x, y])
{
case 0:
arrayColor[x, y] = new Color(0,0,0);
break;
case 1:
arrayColor[x, y] = new Color(0,0,0);
break;
case 2:
arrayColor[x, y] = new Color(0,0,0);
break;
case 3:
arrayColor[x, y] = new Color (0,0,0);
break;
}
}
}
}
public Rectangle[,] GetMap()
{
return rectMap;
}
public Color[,] GetColors()
{
return arrayColor;
}
}
In the for-loop where you're populating roomList, you're not instantiating a new Room each time. You're simply manipulating the same Room object and re-adding it to the list, so roomList will just contain many references to the same Room object. Try removing the room field from your DungeonGenerate class and use a local variable instead:
for (int i = 0; i < amountOfRooms; i++)
{
bool ok = false;
do
{
var room = new Room();
...
roomList.Add(room);
} while (!ok);
}

Categories

Resources