作者itoc, 信區: itoc
標題股市大亨 -- stock.c
時間04/02/03
作者: itoc (League) 看板: plan
標題: 股市大亨
時間: 2004/02/03 Tue 14:49:04 Updated: 2004/03/02
: src/maple/menu.c 適當的選單加入
"bin/stock.so:main_stock", PERM_BASIC, - M_GAME,
"1Stock ♂ 股市大亨 ♀",
: src/include/config.h
+ #define STOCKSHM_KEY 4999 /* 股市 */
#define PIPSHM_KEY 4998 /* 電子雞對戰 */
: src/game/Makefile
SO = ..... stock.so
: src/game/stock.c 新增此程式
/*-------------------------------------------------------*/
/* stock.c ( NTHU CS MapleBBS Ver 3.10 ) */
/*-------------------------------------------------------*/
/* target : 真實股票遊戲 */
/* create : 98/06/21 */
/* update : 01/05/08 */
/* author : dsyan.bbs@forever.twbbs.org */
/* recast : itoc.bbs@bbs.tnfsh.tn.edu.tw */
/*-------------------------------------------------------*/
#if 0
本股市需要配合 bin/stock-open.pl 這程式去抓資料,crontab 如下:
# 每天 3:50PM 轉換股市格式
50 15 * * * bin/stock-open.pl > etc/game/stock_data
etc/game/stock_name 是所有股票的列表,若有新股加入,可以加在最後
etc/game/stock_data 是所有股票的市價,stock-open.pl 會去抓
要使用 stock-open.c 必須裝有 lynx。
#endif
#include "bbs.h"
#ifdef HAVE_GAME
#define STOSUM 2000 /* 遊戲中最多允許幾家公司 */
#define STOCKNAME "股市大亨"
#define FN_STOCK "stock" /* usr 目錄下個人的股市檔案-各股擁有張數 */
#define FN_STOCK_LOG "stock.log"/* usr 目錄下個人的股市檔案-歷史記錄 */
typedef struct
{
char name[STOSUM][12]; /* 所有各家股票的名字 #### 甲乙丙
四個數字、一個空白、三個國字 */
char update[11]; /* 更新日期格式 [90/05/08] */
int price[STOSUM]; /* 價格 */
/* 為了程式方便,23.45 元就儲存成 23450,
即一張(1000股的價格) */
int max; /* 共有幾家公司 */
int maxpage; /* 共需要幾頁 */
} STOCK;
int
main_stock()
{
int own[STOSUM]; /* 對各股的擁有股份 */
char *fname = FN_STOCK_LOG;
char name[12], buf[128];
FILE *fp;
int i, ch, price, num, money, page, max, maxpage;
time_t now;
struct tm *ptime;
STOCK *sto;
if (HAS_STATUS(STATUS_COINLOCK))
{
vmsg(msg_coinlock);
return XEASY;
}
if (!dashf("etc/game/stock_data") || !dashf("etc/game/stock_name"))
{
vmsg("股市檔案遺失,請告訴站長");
return XEASY;
}
/* initialize shared memory */
sto = shm_new(STOCKSHM_KEY, sizeof(STOCK));
/* 讀資料檔 */
fp = fopen("etc/game/stock_data", "r"); /* 讀取目前股價 */
fgets(buf, 30, fp);
if (strcmp(buf, sto->update)) /* 若 shm 中不是今天的資料,才更新 */
{
FILE *fn;
strcpy(sto->update, buf); /* 更新日期 */
ch = 0;
fn = fopen("etc/game/stock_name", "r");
while (fgets(sto->name[ch], 12, fn))
{
fgets(buf, 12, fn);
ch++; /* 借用 ch 作為 stock_name 中的公司數 */
}
fclose(fn);
num = 0;
fn = fopen("etc/game/stock_name", "a");
while (fgets(buf, 80, fp)) /* 從 stock_data 取出股價存入 shm */
{
/* 在本 while 迴圈中,
ch 是指 stock_name 中的公司數,num 是指 stock_data 中的公司數 */
if (buf[0] < '0' || buf[0] > '9')
continue; /* 不合理的格式 */
buf[11] = '\0';
buf[21] = '\0';
num++;
strcpy(name, buf); /* buf[0] ~ buf[11] 為公司名稱 */
price = atoi(buf + 16) * 10; /* buf[16] ~ buf[21] 為價格 */
/* 為了避免網頁的變動或新上市公司,而使 stock_name 和 stock_data
不是一對一對應,是故從 stock_data 中抓到價格以後,要搜尋正確的欄位 */
for (i = 0; i < ch; i++)
{
if (!strcmp(name, sto->name[i]))
break;
}
if (i == ch && ch < STOSUM) /* 如果有新增公司,要寫入 stock_name */
{ /* 如果已經超過 STOSUM,則不得增加 */
strcpy(sto->name[i], name);
fprintf(fn, "%s\n", name);
ch++;
}
sto->price[i] = price;
}
fclose(fn);
sto->max = num;
sto->maxpage = num / 40 + ((num % 40) ? 1 : 0);
/* maxpage = max/40 取天花板函數 */
}
fclose(fp);
max = sto->max;
maxpage = sto->maxpage;
/* 讀取 user 資料檔 */
usr_fpath(buf, cuser.userid, FN_STOCK);
if (fp = fopen(buf, "r")) /* 以前玩過股市,讀取目前所有的股份 */
{
for (i = 0; i < STOSUM; i++)
fscanf(fp, "%d\n", &own[i]);
}
else /* 沒玩過股市,建立新檔 */
{
fp = fopen(buf, "w");
for (i = 0; i < STOSUM; i++)
{
fprintf(fp, "%d\n", 0);
own[i] = 0;
}
}
fclose(fp);
/* 畫面主程式 */
page = 0;
while (1)
{
vs_bar(STOCKNAME);
move(1, 0);
outs("\033[1;31;46m 編號 \033[42m 股 票 名 稱 \033[43m 價 格 \033[45m"
" 持有張數 \033[46m 編號 \033[42m 股 票 名 稱 \033[43m"
" 價 格 \033[45m 持有張數 \033[m");
/* 將本頁的股價分成二欄顯示 */
for (i = 0; i < 20; i++)
{
move(i + 2, 0);
num = page * 40 + i;
if (num + 1 <= max)
{
price = sto->price[num];
ch = (price % 1000) / 10; /* 小數位 */
prints(" %3d) %11s%5d.%02d %5d ",
num + 1, sto->name[num], price / 1000,
(ch < 10 ? ch * 10 : ch), own[num]);
}
num += 20;
if (num + 1 <= max)
{
price = sto->price[num];
ch = (price % 1000) / 10; /* 小數位 */
prints("%3d) %11s%5d.%02d %5d\n",
num + 1, sto->name[num], price / 1000,
(ch < 10 ? ch * 10 : ch), own[num]);
}
}
move(b_lines - 1, 0);
prints(COLOR1 " n:下頁 p:上頁 b:買 s:賣 v:看 e:修 q:離開 "
"錢:%-8d %2d/%2d頁 %-12.10s\033[m",
cuser.money, page + 1, maxpage, sto->update);
get_key:
switch (ch = vkey())
{
/* 以下 name, price, num, i 將不再用到,借來做回答 */
/* itoc.010512.註解: 由於是在下午三點半今天的盤已經關了以後,才去抓今天
的收盤值,所以可以先知道實際今天各股的漲跌,如果不限制交易時間,那麼
將可以以昨天的價格的買進。同理,賣出也要限制時間 */
case 'b': /* buy */
now = time(0);
ptime = localtime(&now);
if (ptime->tm_hour >= 6 && ptime->tm_hour <= 15)
{
vmsg("交易時間為每天 4:00pm 到隔天早上 7:00am .. :)");
break;
}
sprintf(buf, "要買哪家(1-%d)?", max);
vget(b_lines, 0, buf, name, 4, DOECHO);
i = atoi(name);
if (i < 1 || i > max)
break;
price = sto->price[i - 1];
price += price / 300; /* 抽稅千分之三 */
money = price ? cuser.money / price : 0; /* itoc.010716: 預防 price=0 */
sprintf(buf, "要買幾張(1-%d)?", money);
vget(b_lines, 0, buf, name, 4, DOECHO);
num = atoi(name);
if (num < 1 || num > money)
break;
/* 買第 i 號股票 num 張 */
own[i - 1] += num;
money = num * price;
if (cuser.money <= money)
cuser.money = 0;
else
cuser.money -= money;
/* 記錄購買記錄 */
usr_fpath(buf, cuser.userid, fname);
fp = fopen(buf, "a+");
ch = (price % 1000) / 10; /* 小數位 */
fprintf(fp, "[%s] %5d.%02d 買進 %s %d 張 花費 %d\n",
Btime(&now), price / 1000, (ch < 10 ? ch * 10 : ch),
sto->name[i - 1], num, money);
fclose(fp);
break;
case 's': /* sell */
now = time(0);
ptime = localtime(&now);
if (ptime->tm_hour >= 3 && ptime->tm_hour <= 18)
{
vmsg("交易時間為每天 7:00pm 到隔天早上 4:00am .. :)");
break;
}
sprintf(buf, "要賣哪家(1-%d)?", max);
vget(b_lines, 0, buf, name, 4, DOECHO);
i = atoi(name);
if (i < 1 || i > max)
break;
price = own[i - 1];
if (!price) /* 沒有持股不能賣 */
break;
sprintf(buf, "要賣幾張(1-%d)?", price);
vget(b_lines, 0, buf, name, 4, DOECHO);
num = atoi(name);
if (num < 1 || num > price)
break;
/* 賣第 i 號股票 num 張 */
own[i - 1] -= num;
price = sto->price[i - 1];
price -= price / 300; /* 抽稅千分之三 */
money = num * price;
cuser.money += money;
/* 記錄賣出記錄 */
usr_fpath(buf, cuser.userid, fname);
fp = fopen(buf, "a+");
ch = (price % 1000) / 10; /* 小數位 */
fprintf(fp, "[%s] %5d.%02d 賣出 %s %d 張 收入 %d\n",
Btime(&now), price / 1000, (ch < 10 ? ch * 10 : ch),
sto->name[i - 1], num, money);
fclose(fp);
break;
case 'v': /* view history */
usr_fpath(buf, cuser.userid, fname);
more(buf, NULL);
break;
case 'e': /* edit history */
usr_fpath(buf, cuser.userid, fname);
vedit(buf, 0);
break;
case 'p': /* previous page */
case KEY_UP:
case KEY_PGUP:
if (page)
page--;
else
page = maxpage - 1;
break;
case 'n': /* next page */
case KEY_DOWN:
case KEY_PGDN:
if (page + 1 < maxpage)
page++;
else
page = 0;
break;
case 'q':
case KEY_LEFT:
/* 在離開股市時才回存所有股票張數及金錢,節省I/O */
usr_fpath(buf, cuser.userid, FN_STOCK);
fp = fopen(buf, "w");
for (i = 0; i < STOSUM; i++)
fprintf(fp, "%d\n", own[i]);
fclose(fp);
return 0;
default:
if (ch >= '1' && ch <= '9')
{
if (vget(b_lines, 0, "跳至第幾頁:", name, 3, DOECHO))
{
i = atoi(name) - 1;
if (i < maxpage)
page = i;
}
break;
}
goto get_key; /* 不用重繪畫面 */
}
}
}
#endif /* HAVE_GAME */
--
□ 本文章由 itoc 從 pc512-2.EE.NCTU.edu.tw 發表精華選讀←離開[主題上]主題下(k)上篇(j)下篇S/a搜尋 G串列 TAB精華 ↑↓捲 Pg/Space翻