要求輸出扔完色子後各面的數字。


先上GIF截圖

感動,第一次答案被知乎標記成優質答案,更新了一下代碼現在視角上下左右都可以操控了。

要顯示骰子各面數字,最簡單的辦法就是把骰子直接顯示出來。考慮到題主應該是剛接觸C語言,對圖形庫的使用可能還不瞭解,所以我在終端用字元畫了一個骰子出來。所有代碼都在一個c文件中,不依賴任何第三方庫,應該能方便新手學習理解。

操作方法WASD鍵控制顯示方向,R鍵隨機扔骰子,ESC鍵退出。

下面是代碼,在Windows mingw-w64編譯通過。因為有linux用戶留言,所以我更新了一下代碼,現在在Windows,Mac和Linux下都能使用GCC正常編譯使用了。

update:

關於新手能不能理解的問題。其實這份代碼只用到了非常基礎的立體幾何知識,大部分人在大一高數學的比這個難很多。事實上我之前從來沒寫過這類代碼,更不用說什麼圖形學,看我畫線那部分代碼,循環體裡面用浮點乘除就知道這份代碼有非常大的改進空間。編程的初學者很多時候覺得高大上的東西,做一個簡單Demo用到的知識其實比大學考試考得還少。只有不斷地嘗試,才能提高自己的編程能力。

#include &
#include & #include &
#ifdef _WIN32
#include &
#include &
#endif
#include &

struct point
{
double x[3];
};
struct edge
{
struct point a, b;
};
struct plane
{
// k[0]x+k[1]y+k[2]z=d
double k[3], d;
};
typedef struct point point;
typedef struct edge edge;
typedef struct plane plane;

#define PI 3.14159265358
point cubePoint[8] = {{0.5, 0.5, -0.5}, {0.5, 0.5, 0.5}, {-0.5, 0.5, 0.5}, {-0.5, 0.5, -0.5}, {0.5, -0.5, -0.5}, {0.5, -0.5, 0.5}, {-0.5, -0.5, 0.5}, {-0.5, -0.5, -0.5}};
int cubePlaneGroup[6][4] = {{0, 1, 5, 4}, {1, 2, 6, 5}, {2, 3, 7, 6}, {3, 0, 4, 7}, {0, 1, 2, 3}, {4, 5, 6, 7}};
int PlaneNumber[6];
int table[6][6] = {{2, 4, 5, 3, 1, 6}, {1, 3, 6, 4, 2, 5}, {1, 5, 6, 2, 3, 4}, {2, 6, 5, 1, 4, 3}, {4, 6, 3, 1, 5, 2}, {3, 5, 4, 2, 6, 1}};
double cube2dPoint[8][2];
int cubeDisplayPoint[8][2];
point camera = {2, 1.5, 2};
point cameraY = {-2, 2, 2};
const double cameraR = 3.5;
double displayPlaneSize = 4;
const int displayPixelSize = 30;
// 終端英文字元寬高比例需要1:2
const int displayPixelWidth = 60;
char displayBuff[30][60];
double angleA = -PI / 4;
double angleB = PI / 4;
double speed = 0.05;

void draw();

#ifndef _WIN32
#include &
#include &
char getch()
{
char buf = 0;
struct termios old = {0};
fflush(stdout);
if (tcgetattr(0, old) &< 0) perror("tcsetattr()"); old.c_lflag = ~ICANON; // local modes = Non Canonical mode old.c_lflag = ~ECHO; // local modes = Disable echo. old.c_cc[VMIN] = 1; // control chars (MIN value) = 1 old.c_cc[VTIME] = 0; // control chars (TIME value) = 0 (No time) if (tcsetattr(0, TCSANOW, old) &< 0) perror("tcsetattr ICANON"); if (read(0, buf, 1) &< 0) perror("read()"); old.c_lflag |= ICANON; // local modes = Canonical mode old.c_lflag |= ECHO; // local modes = Enable echo. if (tcsetattr(0, TCSADRAIN, old) &< 0) perror("tcsetattr ~ICANON"); return buf; } #endif plane getDisplayPlane() { double d = 0; for (int i = 0; i &< 3; i++) { d += camera.x[i] * camera.x[i]; } return (plane){{camera.x[0], camera.x[1], camera.x[2]}, -d}; } point getDisplayCenter() { return (point){-camera.x[0], -camera.x[1], -camera.x[2]}; } point getVector(point a, point b) { return (point){b.x[0] - a.x[0], b.x[1] - a.x[1], b.x[2] - a.x[2]}; } double innerProduct(point a, point b) { double res = 0; for (int i = 0; i &< 3; i++) { res += a.x[i] * b.x[i]; } return res; } point crossProduct(point a, point b) { return (point){a.x[1] * b.x[2] - a.x[2] * b.x[1], a.x[2] * b.x[0] - a.x[0] * b.x[2], a.x[0] * b.x[1] - a.x[1] * b.x[0]}; } double getLength(point a) { double sqr = 0; for (int i = 0; i &< 3; i++) { sqr += a.x[i] * a.x[i]; } return sqrt(sqr); } point getPoint(edge a, plane b) { point res; for (int i = 0; i &< 3; i++) { double k = b.k[i]; double d = b.d; for (int j = 0; j &< 3; j++) { if (i != j) { k += b.k[j] * (a.a.x[j] - a.b.x[j]) / (a.a.x[i] - a.b.x[i]); d -= b.k[j] * (a.b.x[j] * a.a.x[i] - a.a.x[j] * a.b.x[i]) / (a.a.x[i] - a.b.x[i]); } } res.x[i] = d / k; } return res; } void initCamera() { camera.x[0] = cos(angleA) * cos(angleB) * cameraR; camera.x[1] = sin(angleA) * cameraR; camera.x[2] = cos(angleA) * sin(angleB) * cameraR; cameraY.x[0] = cos(angleA + PI / 2) * cos(angleB) * cameraR; cameraY.x[1] = sin(angleA + PI / 2) * cameraR; cameraY.x[2] = cos(angleA + PI / 2) * sin(angleB) * cameraR; } void drawLine(const int a[2], const int b[2]) { if (a[0] == b[0] a[1] == b[1]) { return; } int step[2]; for (int i = 0; i &< 2; i++) { if (a[i] - b[i] &> 0)
{
step[i] = -1;
}
else if (a[i] - b[i] == 0)
{
step[i] = 0;
}
else
{
step[i] = 1;
}
}
if (abs(a[0] - b[0]) &> abs(a[1] - b[1]))
{
int j = a[1];
double k = (a[1] - b[1]) * 1.0 / (a[0] - b[0]);
for (int i = a[0] + step[0]; (b[0] - i) / step[0] &> 0; i += step[0])
{
if (fabs(1.0 * (j + step[1] - a[1]) / (i - a[0]) - k) &< fabs(1.0 * (j - a[1]) / (i - a[0]) - k)) { j += step[1]; } if (displayBuff[i][j] == 0) { displayBuff[i][j] = *; } } } else { int j = a[0]; double k = (a[0] - b[0]) * 1.0 / (a[1] - b[1]); for (int i = a[1] + step[1]; (b[1] - i) / step[1] &> 0; i += step[1])
{
if (fabs(1.0 * (j + step[0] - a[0]) / (i - a[1]) - k) &< fabs(1.0 * (j - a[0]) / (i - a[1]) - k)) { j += step[0]; } if (displayBuff[j][i] == 0) { displayBuff[j][i] = *; } } } } void fswap(double *a, double *b) { double tmp = *a; *a = *b; *b = tmp; } void fill(int r, int c, char ch) { if (r &< 1 || c &< 1 || r &>= displayPixelSize || c &>= displayPixelWidth)
{
return;
}
if (displayBuff[r][c] != 0)
{
return;
}
displayBuff[r][c] = ch;
fill(r + 1, c, ch);
fill(r - 1, c, ch);
fill(r, c + 1, ch);
fill(r, c - 1, ch);
}

int sqr(int x)
{
return x * 2;
}

void drawCube()
{
memset(displayBuff, 0, sizeof(displayBuff));
double dis[6][2] = {0};

for (int i = 0; i &< 6; i++) { dis[i][0] = i; for (int j = 0; j &< 4; j++) { dis[i][1] += getLength(getVector(camera, cubePoint[cubePlaneGroup[i][j]])); } for (int j = i - 1; j &>= 0; j--)
{
if (dis[j][1] &> dis[j + 1][1])
{
fswap(dis[j][1], dis[j + 1][1]);
fswap(dis[j][0], dis[j + 1][0]);
}
}
}
for (int i = 0; i &< 6; i++) { int planeId = (int)dis[i][0]; int hide = 0; int xy[4][2]; for (int j = 0; j &< 4; j++) { memcpy(xy[j], cubeDisplayPoint[cubePlaneGroup[planeId][j]], sizeof(xy[j])); } for (int j = 0; j &< 4; j++) { if (displayBuff[xy[j][0]][xy[j][1]] != 0 displayBuff[xy[j][0]][xy[j][1]] != +) { hide = 1; } } if (hide) { continue; } for (int j = 0; j &< 4; j++) { drawLine(xy[j], xy[(j + 1) % 4]); } for (int j = 0; j &< 4; j++) { displayBuff[xy[j][0]][xy[j][1]] = +; } int center[2]; if (sqr(xy[0][0] - xy[2][0]) + sqr(xy[0][1] - xy[2][1]) &> sqr(xy[1][0] - xy[3][0]) + sqr(xy[1][1] - xy[3][1]))
{
center[0] = (xy[0][0] + xy[2][0]) / 2.0 + 0.5;
center[1] = (xy[0][1] + xy[2][1]) / 2.0 + 0.5;
}
else
{
center[0] = (xy[1][0] + xy[3][0]) / 2.0 + 0.5;
center[1] = (xy[1][1] + xy[3][1]) / 2.0 + 0.5;
}
fill(center[0], center[1], );
displayBuff[center[0]][center[1]] = 0 + PlaneNumber[planeId];
}
}

void randomCast()
{
int tableIdx = rand() % 6;
int offset = rand();
for (int i = 0; i &< 4; i++) { PlaneNumber[i] = table[tableIdx][(i + offset) % 4]; } for (int i = 4; i &< 6; i++) { PlaneNumber[i] = table[tableIdx][i]; } } void cls() { #ifdef _WIN32 COORD pos = {0, 0}; HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleCursorPosition(out, pos); #endif #ifndef _WIN32 printf(" 33c"); #endif } void draw() { initCamera(); for (int i = 0; i &< 8; i++) { point p = getPoint((edge){camera, cubePoint[i]}, getDisplayPlane()); point v = getVector(getDisplayCenter(), p); double vlen = getLength(v); double cos = innerProduct(v, cameraY) / getLength(cameraY) / vlen; double sin = sqrt(fabs(1 - cos * cos)); int largeThanPi = innerProduct(crossProduct(cameraY, v), camera) &< 0; double y = cos * vlen; double x = -sin * vlen; if (largeThanPi) { x *= -1; } cube2dPoint[i][0] = x; cube2dPoint[i][1] = y; cubeDisplayPoint[i][1] = ((x + displayPlaneSize / 2) / displayPlaneSize * displayPixelSize * 2 + 0.5); cubeDisplayPoint[i][0] = ((y + displayPlaneSize / 2) / displayPlaneSize * displayPixelSize + 0.5); } drawCube(); char output[displayPixelSize * displayPixelSize * 2 + displayPixelSize + 1]; int idx = 0; for (int i = 0; i &< displayPixelSize; i++) { for (int j = 0; j &< displayPixelSize * 2; j++) { if (displayBuff[i][j] == 0) { displayBuff[i][j] = .; } output[idx++] = displayBuff[i][j]; } output[idx++] = ; } output[idx++] = 0; cls(); printf("%s", output); } void start() { while (1) { int key = getch(); switch (key) { case 27: { exit(0); } case w: { angleA -= speed; break; } case s: { angleA += speed; break; } case a: { angleB += speed; break; } case d: { angleB -= speed; break; } case r: randomCast(); break; }; draw(); } } int main() { #ifdef _WIN32 system("cls"); #endif srand(time(0)); randomCast(); draw(); start(); }


和別人理解的不一樣,這其實是一道剛體動力學加接觸碰撞的計算程序。

建立一個剛體動力學加接觸力方程:

[公式]

先網格化色子模型,加上接觸檢測模塊,結合上述常微分方程,就可以實現立方體色子的動力學歷程的模擬,由於存在接觸阻尼,色子必然會停止在地面上。所以這是真正的現實上的隨機過程。


心血來潮,我就來一份代碼

#include &
#include &
#include &

void showDice(int* dice,size_t num) {
printf(" -----------
");
printf(" / /|
");
printf(" / %d / |
",dice[0]);
printf(" / / |
");
printf(" ----------- |
");
printf(" | | %d |
",7-dice[2]);
printf(" %d | %d | /
", dice[2],dice[1]);
printf(" | | /
");
printf(" | | /
");
printf(" -----------/
");
printf(" %d
", 7-dice[0]);
printf("
");
return;
}

int main()
{
int data[6][4] = {
{2, 3, 5, 4},
{6, 3, 1, 4},
{1, 2, 6, 5},
{1, 5, 6, 2},
{1, 3, 6, 4},
{2, 4, 5, 3} };
int dice[3];
while (1)
{
printf("

==================================
");
unsigned int value = 0;
srand(time(NULL));
value = rand() % 6; //取值範圍是0~5

printf("擲到的點數為:%d
", value + 1); //因為取值範圍是0~5,所以需要加一個一
dice[0] = value + 1; //top side

unsigned int value_2 = 0;
value_2 = rand() % 4; //找環的起點,取值範圍是0~3,四個數中隨機取一個

dice[1] = data[value][value_2]; //front side
dice[2] = data[value][(value_2 + 1) % 4]; //left side
showDice(dice, 3);
printf("再扔一次?
");
getchar();
}
return 0;
}

代碼是在 @scythe 基礎上修改的,就是多加了一個控制檯簡陋的繪圖輸出。

效果如下:

代碼原理,其實已經說得蠻清楚了,對頂部元素隨機,對正面元素隨機,找到其對應的中間元素順序即可。


我一開始以為題主只是想做一個隨機產生1~6的數字的程序,但是看了一下問題描述,似乎有點意思。題主要求的是,輸出各面的數字。

那麼我們先來觀察一下骰子

不難發現,它有6個面,且每個面都有與之對應的四個側面

例如,把1點作為正面,那麼它的四個側面,按照順時針的順序排列就是 : 2,3,5,4

且這個排列是一個環,從2點開始看是2,3,5,4,而如果從3點開始看就是 3,5,4,2也就是說,從不同的角度去看骰子,四面的狀態是不一樣的。

來列個表,當擲骰子的時候向上的面是 n 時,側面的環的狀態,注意,雖然此時四個側面是一個環,但底部一定是固定不變的,且頂部的數和底部的數相加等於七。

1:2,3,5,4

2:6,3,1,43:1,2,6,54:1,5,6,25:1,3,6,46:2,4,5,3有了這個表就很簡單了首先定義一個6行4列的數組,然後產生一個隨機數,範圍是1~6,獲取到某一行,然後再產生一個隨機數,從這一行的環中某一個數開始列印就完事了。

#include &
#include &
#include &

int main()
{
int data[6][4] = {
{2, 3, 5, 4},
{6, 3, 1, 4},
{1, 2, 6, 5},
{1, 5, 6, 2},
{1, 3, 6, 4},
{2, 4, 5, 3}};

while (1)
{
printf("

==================================
");
unsigned int value = 0;
srand(time(NULL));
value = rand() % 6; //取值範圍是0~5

printf("擲到的點數為:%d
", value + 1); //因為取值範圍是0~5,所以需要加一個一

unsigned int value_2 = 0;
value_2 = rand() % 4; //找環的起點,取值範圍是0~3,四個數中隨機取一個

printf("正面是:%d
", data[value][value_2]);
printf("左面是:%d
", data[value][(value_2 + 1) % 4]);
printf("後面是:%d
", data[value][(value_2 + 2) % 4]);
printf("右面是:%d
", data[value][(value_2 + 3) % 4]);
printf("再扔一次?
");
getchar();
}
return 0;
}

運行結果:


蹲一個openGL渲染的答案

最好要有真實重力物理引擎

幫你們開好頭了

#include&

應評論區要求,結尾也幫你們寫好了

return 0;
}

來張做完的效果圖

再來個帶光追的


推薦閱讀:
相關文章