Week 7 (2023/11/16)¶
今日やること
- バージョン管理(Git/GitHub)
- 入出力のまとめ
まずはじめに¶
- UTAS上のアンケートの記入をお願いします。
バージョン管理(Git/GitHub)¶
バージョン管理について勉強しましょう。独立したページにまとめました。
入出力のまとめ¶
Cには様々な入出力方法があります。これまでの講義ではprintf
による書式付き表示、putchar/getchar
による一単語の入出力、argv
による引数受付、を学びました。
Cには他にも様々な入出力関数があります。ここでは有名どころをいくつか紹介し、簡単にまとめておきましょう。
書式付き入出力:printf, scanf¶
printf
は書式付きの表示関数です。すなわち、何を表示するかを文字列で指定して、その中に変数を埋め込むことができます(これを書式文字列といいます)。
その内容を、標準出力に表示します。
これは既にみなさんよく知っていますね。
printf("ID:%d\n", 13); // 表示する書式文字列を渡し、その中に変数を埋め込む
$ ./a.out
ID:13 <- 標準出力に表示。すなわちターミナルに表示。
$ ./a.out > hoge.txt <- リダイレクトでファイルに書き出す。hoge.txtというファイルが出来てその中にID:13が書かれる。
<- ターミナルには表示されない。
printf
してそれをリダイレクトで吐き出すことです。
さて、printf
はすなわち変数を書式文字列に埋め込み標準出力に表示するものでした。
scanf
はこの逆の操作をします。
すなわち、標準入力からのストリームを、書式文字列で受け取り、変数に与えます。
int n;
scanf("ID:%d", &n);
printf("n is %d\n", n); // 確認
&n
すなわちint
のアドレスを与えています。
上記を実行すると、端末が入力を受け付ける待機状態になります。そこでID:13
のように書式文字列に沿って打ち込むと、
n
には13
がセットされます。
コラム
なぜここではn
のアドレスを渡す必要があるのでしょうか?
Cの基本ルールとして、関数が呼び出し元の変数を編集するためにはポインタを経由しないといけなかったことを思い出しましょう。
ここではアドレスをポインタ経由で渡すので、scanf
関数は呼び出し元のn
を変更できます。
ここで注意として、入力でミスがあると正しく値がセットされません。例えばID :13
と入力する(空白を打ってしまうミス)
と、その入力は書式文字列と合わないので、n
が正しく読み込まれません。
また、書式のルールはprintf
に似ているのですが色々違います。例えば、printf
では%f
でdoubleもfloatも表示できましたが、
scanf
では%f
はfloat用で、doubleには%lf
を使います。
以上により、scanfは簡単で便利なのですが、非常にミスが起きやすいので注意が必要です。
標準出力、リダイレクト、パイプ¶
さて、scanf
は標準入力からストリームを読み込みます。すなわち、キーボードからの入力を受け付けます。
標準入力から受け付けるものは、以下の二つに変更出来たことを思い出しましょう:(1) リダイレクトを使ってファイルからの読み込み、(2) パイプを使って実行時に渡す
リダイレクトでファイルから読み込む場合を考えましょう。上のprintfのときに作った、ID:13
とだけ書いてあるファイルhoge.txt
を準備しましょう。
そのうえで、次を実行します
$ ./a.out < hoge.txt
hoge.txt
からに変更されます。こうすることで、キーボード入力ではなく、ファイルから入力を受け付けることができます。
次にパイプについて思い出しましょう。
$ echo ID:13 | ./a.out
echo
により「ID:13」という単語列がecho
の標準出力に吐き出されます。これがパイプを通してa.out
の標準入力に
入ります。結論として、echo
と組み合わせることで、コマンドの実行時にキーボード入力と同じものを渡すことができます。
これはscanfに限らず、標準入力(キーボード)から入力を受けつけるものは、上記のようにリダイレクトとパイプを使うことができます。
一単語の入出力:putchar, getchar¶
一単語の入出力は week3ですでにやりましたね。以下のようになります。
int c = 'a'; // "a"ではないことに注意
putchar(c);
\n
)もしません。
コラム
'a'
のような文字にはchar
を使ってきましたが、char
は単なる小さな整数なので、int
で表現しても問題ありません。putchar
の引数、およびgetchar
の返り値の型はint
です。
また、標準入力(キーボード)から一語を読み込むときは次のようにすることを既に習いました。
int ret;
ret = getchar();
getchar
も、リダイレクトやパイプを使うことができることも既に習った通りです。
getchar
は一語ずつ読み込むので動作が簡便ですが、たとえば三桁の数字を入力しようと「112
Enter」のようにしても、ret
には'1'
だけが入る点に注意してください。
引数からの入力:argv¶
次に、引数からの入力について復習しましょう。コマンドを実行するとき、その後ろに空白を伴って引数を入力することができます。
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[]) {
if (argc != 4) {
printf("Error. #arg must be 3: method, seed, and v0\n");
return -1;
}
char *method = argv[1];
int seed = atoi(argv[2]);
double v0 = atof(argv[3]);
printf("method: %s\nseed: %d\nv0: %f\n", method, seed, v0);
// do something here
}
int
やdouble
であればatoi
やatof
で変換できます。
このプログラムは以下のように実行できます。
$ ./a.out runge_kutta 123 0.5
method: runge_kutta
seed: 123
v0: 0.500000
argv
で引数を受け付けてプログラムの挙動を制御することは極めて一般的です。
コラム
復習ですが、上記ではseed
とv0
には値が普通に入りますが、method
はポインタなので、"runge_kutta"
という文字列そのものが入るわけではありません。どこかに確保された"runge_kutta"
という文字列の先頭アドレスがargv[1]
に入っており、そのアドレスをmethod
にコピーしているだけです。なので、method
はどこかに確保されている文字列をポイントしているだけです。
コラム
argv
のように、プログラム起動時に引数を与えてその挙動をコントロールすることは極めて一般的です。
例えばマニュアルを意味するman
コマンドを使い $ man ls
とすると、ls
コマンドに関する引数オプションの説明が表示されます。
この画面は上下キーで移動でき、Qを押すと終了します。
自分の書くプログラムに対しても、そのように引数を読み込めるようにしておくと有益です。これについて、例えばpythonではデフォルトで 引数処理を簡単に設定できる機能があります。
ファイルの読み書き:fopen, fclose, fprintf, fscanf, etc¶
さて、上ではリダイレクトを使ってファイルに対する読み書きを行ってきましたが、
それは標準出力を無理やり変更したものでした。
よりキチンとファイルに出力するためには、ファイルを操作する関数を使います。fopen
やfscanf
のように、先頭にf: file
がついているものです。
ファイルの読み書きについてはソフトウェア2で詳しくやりますので深入りしませんが、例えば次のようにします。
char filename[] = "hoge.txt";
// ここからがファイル読み込みの定型文
FILE *fp;
fp = fopen(filename, "r"); // "read"
if (fp == NULL) {
printf("Cannot open %s\n", filename);
return -1;
}
// ここでfpを経由してファイルからデータを読み込む
int n;
fscanf(fp, "ID:%d", &n);
printf("n is %d\n", n); // 確認
// ここでfpの後始末
fclose(fp);
ここで、FILE *fp
とは、ファイルの操作をつかさどるファイルポインタというものです。
fopen
という関数でファイルを開きます。その「開いているファイルを指し示す印」がfp
です。
もしファイルのオープンに失敗していると、fp
はNULL
になります。
ここで、fopen
の二つ目の引数は、どういうモードでファイルを開くかを決定します。
読み込むだけのときはread: "r"
に、書き込むときはwrite: "w"
に、追記モードにするときはappend: "a"
にします。
ファイルのオープンに成功した場合、fp
を経由して、先頭にf
がつく関数で読み書きができます。
たとえばfscanf(fp, ...)
では、scanf
と似たような挙動をしますが、その読み込み元が標準入力(キーボード)ではなくfp
、すなわち
今開いたファイルになります。
全ての処理が終ったあとは、ファイルポインタを開放しておくとよいです。それがfclose
です。
上の例では開放しなくてもプログラムが終了しますが、例えば別の関数の中でfopen
などを行った場合は、ちゃんとfclose
しておかないと
問題が発生するかもしれません。
上はファイルを読み込む例でしたが、下は書き込む例です。
#include <stdio.h>
int main (int argc, char *argv[]) {
// argvを使い、引数からファイル名をうけとる
if (argc != 2) {
printf("Error. #arg must be 1: filename\n");
return -1;
}
char *filename = argv[1];
// ここからがファイル書き込みの定型文
FILE *fp;
fp = fopen(filename, "w"); // "write"
if (fp == NULL) {
printf("Cannot open %s\n", filename);
return -1;
}
// ここでfpを経由してファイルにデータを書き込む
fprintf(fp, "ID:%d", 13);
// ここでfpの後始末
fclose(fp);
}
$ ./a.out fuga.txt
のようにして、fuga.txt
に情報を書き込むことができます。
ファイルへの入出力には他にもいろいろ方式があります。とくに、上記はテキストの読み書きでしたが、数値を実際に保存するときはバイナリとして
保存します。これはwrite binary: wb
のようなモードを使います。
そして、fread
やfwrite
といった関数を使います。
個人的なオススメ¶
さて、色々紹介して疲れたと思いますので、実際にこれから講義や研究でコードを書く上でどれを使えばいいかについて、以下に松井のオススメ例を示します。
- 短い入力を受け付けるときは、
argv
で受け取って文字列を解析する。文字列を読みたい場合はそのままだし、数値であればatoi
やatof
だけで読み込めると楽(というか、argv
で受け付けるべきなのはそういう短いものだけ) - 長い入力を受け付けるときは、
fread
やfopen
で開く。より正確には、argv
でファイル名を受け取り、それをfread
などで開いて読み込む。 - とりあえず簡単に出力したいときは、リダイレクトを使う
$ ./a.out > out.txt
- ちゃんと出力するときは、
fwrite
やfopen
で保存する
やってみよう(20分)
上記を写経しましょう
クイズ
- バージョン管理のクイズで作ったリポジトリに、以下の
compute_squares.c
を追加しましょう。- 引数から配列長
N
とファイル名filename
を受け取る filename
の最初の行には、N
の値を書き込む。0, 1, 2, .., N-1
について、それぞれ二乗する- 二乗したそれぞれについて、
filename
に対し、一行に一つの数字が入るようにして書き出す - 出力例:
./compute_squares 4 out.txt
- こうすると
out.txt
は以下のようになる4 0 1 4 9
- 引数から配列長
- 上で出力されたファイルを読み込み、さらに整数を一つ受け取り、その整数が
out.txt
に書かれたものがチェックする関数check_val.c
を追加しましょう。これもレポジトリに追加しましょう。- 引数からファイル名
filename
と整数val
を受け取る filename
からN
と数列を読み込み、val
が含まれているか調べる。- 例えば上の出力例の通り
N=4
でout.txt
が作られているとします。 - 出力例;
./check_val out.txt 12
<- 含まれない、というなんらかのメッセージを吐く - 出力例;
./check_val out.txt 9
<- 含まれる、というなんらかのメッセージを吐く
- 引数からファイル名
答え
- 例えば以下
#include <stdio.h> #include <stdlib.h> int main (int argc, char *argv[]) { // argvを使い、引数からファイル名をうけとる if (argc != 3) { printf("Error. #arg must be 2: N and filename\n"); return -1; } int N = atoi(argv[1]); char *filename = argv[2]; // ここからがファイル書き込みの定型文 FILE *fp; fp = fopen(filename, "w"); // "write" if (fp == NULL) { printf("Cannot open %s\n", filename); return -1; } // ここでfpを経由してファイルにデータを書き込む fprintf(fp, "%d\n", N); for (int n = 0; n < N; ++n) { fprintf(fp, "%d\n", n * n); } // ここでfpの後始末 fclose(fp); }
#include <stdio.h> #include <stdlib.h> int main (int argc, char *argv[]) { // argvを使い、引数からファイル名をうけとる if (argc != 3) { printf("Error. #arg must be 2: N and filename\n"); return -1; } char *filename = argv[1]; int val = atoi(argv[2]); // ここからがファイル読み込みの定型文 FILE *fp; fp = fopen(filename, "r"); // "read" if (fp == NULL) { printf("Cannot open %s\n", filename); return -1; } // ここでfpを経由してファイルにデータを読み込む int N; fscanf(fp, "%d", &N); int vals[N]; for (int n = 0; n < N; ++n) { fscanf(fp, "%d", vals + n); // vals[n]に読み込む } // ここでfpの後始末 fclose(fp); // 値のチェック printf("N: %d\n", N); for (int n = 0; n < N; ++n) { printf("%d, ", vals[n]); } printf("\n"); for (int n = 0; n < N; ++n) { if (vals[n] == val) { printf("Found the value!: %d\n", val); return 0; } } printf("Cannot found the value: %d\n", val); return 0; }
最終課題¶
それでは最終課題です。
- 締切は 二週間後の深夜23:59までです(今回の場合、2023/11/29, 23:59)
- 宿題リンクをslackで配付します。このリンクは公開しないでください。
注意:Google Cloud Shell Editorの内容は、しばらく使っていないと消えてしまいます。重要なファイルは、自分の手元のローカルマシンにダウンロードしておいてください。
week7_1¶
最近流行った、スイカゲームライクな落ちものゲームを実装しましょう。
- スイカゲームの公式サイト
- wikipediaによる説明
- YouTubeで「スイカゲーム」で調べると、配信者が遊んでいる様子が見れます。
スイカゲームは落ちものゲームです。今回は簡易版を作ります。ここでは以下のようなルールとします。
- 「1」と描かれたオブジェクトを横方向に動かす。
- オブジェクトを落とす。
- 落ちたオブジェクトは積み重なっていく。
- 「1」が隣り合うと、それらは消えて、「2」が生まれる。
- 生まれた「2」が別の「2」と隣り合う場合、それらは消えて「3」になる。
- 上記のように、消滅生成は連鎖的に起こる。
以下、出来るところまでやってみてください。
main.c
には以下のような雛形コードが書いてあります。
#include <stdio.h>
#include <stdlib.h>
#define BOARD_SIZE 5
typedef struct point {
int x; // x座標
int y; // y座標
int v; // 進化度
} Point;
typedef struct board {
char field[BOARD_SIZE][BOARD_SIZE + 1];
} Board;
void print_board(Board b, Point p) {
b.field[p.y][p.x] = p.v + '0';
for(int i = 0; i < BOARD_SIZE; ++i) {
printf("%s\r\n", b.field[i]);
}
}
int main (int argc, char *argv[]) {
Board b = {.field = {
"-----",
"-----",
"-----",
"-----",
"-----"
}};
Point p = {.x = 0, .y = 0, .v = 1}; // 現在操作しているオブジェクト
int c = '_'; // Dummy
system("/bin/stty raw onlcr"); // enterを押さなくてもキー入力を受け付けるようになる
while (1) {
system("clear");
printf("Press '.' to close\r\n");
printf("You pressed '%c'\r\n", c);
if (c == 'd') {
++p.x;
}
print_board(b, p);
if ((c = getchar()) == '.') { // '.' を押すと抜ける
break;
}
}
system("/bin/stty cooked"); // 後始末
return 0;
}
これを実行すると以下のような画面になります。
Press '.' to close
You pressed '_'
1----
-----
-----
-----
-----
この画面がパズル盤面を表しています。ここではフィールドの大きさは「5マス x 5マス」です。-
がオブジェクトを移動できるフィールド範囲です。
1
がオブジェクトです。ここではd
を押すと右に一歩進むようになっています。d
を押してみましょう。
Press '.' to close
You pressed 'd'
-1---
-----
-----
-----
-----
このように、右に一歩移動しました。これは、キー入力を受け付ける度にsystem("clear")
で画面をリフレッシュすることで実現しています。
ここで、ピリオド(.
)を押すとゲームが終了するようになっています。
ここで注意があります。以下の二行の表記があると思います。
system("/bin/stty raw onlcr"); // enterを押さなくてもキー入力を受け付けるようになる
system("/bin/stty cooked"); // 後始末
この表記により、わざわざエンターキーを押さなくても、すぐにキー入力を受け付けてくれるようになっています。(参考リンク)
注意として、
- この表記で囲まれている間は、改行するときに
\n
ではなく\r\n
とする必要があります。 - 終了させるときに、Ctrl+C で終了できなくなります。なので、ここではピリオドを終了キーとして用いています。
ということがありますので気を付けてください。
この雛形コードを拡張していってもいいですし、全く別のコードを書いても大丈夫です。自動採点をパスできるのであればなんでも大丈夫です。
また、string.h
やstdlib.h
など、使えるものはなんでも使っていいです。
さて、それでは以下のタスクを出来るところまでやってみてください。
課題A: 左右移動¶
現在、d
を押すと右にすすむということしかできません。また、右ハジに到達してさらに右に進むとおかしなことになります。
ここで以下を実装してください。
a
を押すと左に進む- 右ハジに到達してさらに
d
を押しても移動しない - 左ハジに到達してさらに
a
を押しても移動しない
この結果、./a.out
したあとにdddddaaaaa
と入力すると最初の位置に戻ってくるようになります。
このことは、以下のようなコマンドで確かめることが出来ます。
echo 'dddddaaaaa.' | ./a.out 2>/dev/null
Press '.' to close
You pressed 'a'
1----
-----
-----
-----
-----
ここでは、echo
をパイプで繋げることで、手打ちでキーボード入力する部分を記述しています。また、最後のピリオドを忘れるとプログラムが終了しなくなるので注意してください。終了しなくなった場合、Google Cloud Shell Editorだと、Ctrl+Cを連打しまくるとそのうち止まります。
コラム
ちなみにここで2>/dev/null
の部分は、
- 標準エラー出力である
2
の宛先を、 >
を使って、画面ではなく別の部分に飛ばす。- 飛ばす先は
/dev/null
である
ということを意味しています。今回は詳細は省きますが、プログラムの出力には「標準出力」と「標準エラー出力」の2種類があります。どちらもデフォルトでは画面(ターミナル)に出力されるので特に違いを意識しません。エラーに関するものは「標準エラー出力」に出力されています。今回はその宛先を/dev/null
という「ゴミ箱」に変えました。
どういうことかというと、この部分をつけずにプログラムを実行すると /bin/stty: 'standard input': Inappropriate ioctl for device
のようなエラーメッセージが表示されてしまうと思います。
今回は自動採点でこの部分を省きたいので、明示的に標準エラー出力を変更しました。詳細についてはここやここを読んでください。
さて、自動採点を行うときは、system("clear")
で画面外に飛ばした部分も含めて、以下のようになります。一行目には謎のコマンドがいろいろ表示されることもあるのですが、この部分はstty
に不随してくるゴミなので、気にしないでください。採点は一番下の7行のみで行います。
課題Aの自動採点
入力コマンド:
echo 'aadddddda.' | ./a.out 2>/dev/null
Press '.' to close
You pressed 'a'
---1-
-----
-----
-----
-----
課題B: 落下と復帰¶
さて、次はs
キーを押すと真下まで落下するようにしましょう。
- 二歩右に移動
s
を押して落下- このとき自分は落下したので表示されない
というパスを考えます。この結果、オブジェクトは真ん中の真下に落下します。これを実現してください。自動採点結果は以下です。
課題Bの自動採点
入力コマンド:
echo 'dds.' | ./a.out 2>/dev/null
Press '.' to close
You pressed 's'
-----
-----
-----
-----
--1--
課題C: 落下した後に復帰¶
落下した直後に何かキーを押すと、復帰するようにしてください。すなわち、
- 1歩右に移動
s
を押して落下- 何かキーを押すと左上にあらたにオブジェクトが発生
- 3歩右に移動
s
を押して落下- 何かキーを押すと左上に発生
を実現してください。自動採点結果は以下になります。ここでは最後の14行をチェックします。ここで、「何かのキー」にはd
やs
も含むので注意してください。
課題Cの自動採点
入力コマンド:
echo 'dsxdddsd.' | ./a.out 2>/dev/null
Press '.' to close
You pressed 's'
-----
-----
-----
-----
-1-1-
Press '.' to close
You pressed 'd'
1----
-----
-----
-----
-1-1-
課題D: 1が隣り合うと2に¶
1が隣り合うと、それらは消え、そしてもともと1があった位置(今ふってきたものではなく、最初からおいてあった側のもの)に2が発生するようにしましょう。すなわち、
s
を押して落下- 何かキーを押す
- 1歩右に移動
s
を押して落下(ここでは1が並ぶ)- 何かキーを押す(ここで、1が消滅し、2が発生。このとき、左上の初期オブジェクトはまだナシ)
- 何かキーを押す(ここで左上の初期オブジェクトが発生。初期状態に戻る)
としてください。最後の21行について以下の自動採点があります。
課題Dの自動採点その1
入力コマンド:
echo 'sxdsxx.' | ./a.out 2>/dev/null
Press '.' to close
You pressed 's'
-----
-----
-----
-----
11---
Press '.' to close
You pressed 'x'
-----
-----
-----
-----
2----
Press '.' to close
You pressed 'x'
1----
-----
-----
-----
2----
もう一個自動採点をやってみましょう。 最下段が
1-1--
のときに、1と1の間に1を落とした場合、右とくっつくか左とくっつくかで任意性があります。このような場合は、左とくっつくようにしてください。
課題Dの自動採点その2
入力コマンド:
echo 'sdddsddsd.' | ./a.out 2>/dev/null
Press '.' to close
You pressed 's'
-----
-----
-----
-----
111--
Press '.' to close
You pressed 'd'
-----
-----
-----
-----
2-1--
課題E: 縦方向でも1が並ぶと2になる¶
さて、縦方向でも同様に、1が並んだ場合に2になるようにしましょう。
課題Eの自動採点その1
入力コマンド:
echo 'ssss.' | ./a.out 2>/dev/null
Press '.' to close
You pressed 's'
-----
-----
-----
1----
1----
Press '.' to close
You pressed 's'
-----
-----
-----
-----
2----
ちなみに、「2」の上に「1」が来た場合は普通に積みあがるようにしましょう。
課題Eの自動採点その2
入力コマンド:
echo 'sssssss.' | ./a.out 2>/dev/null
Press '.' to close
You pressed 's'
1----
-----
-----
1----
2----
また、落下した1に対し左右および下が全て1だった場合、は、「下」「左」「右」の順番にくっついてください。つまり、以下です。
課題Eの自動採点その3
入力コマンド:
echo 'ssssssdddsdddsddddsddsddss.' | ./a.out 2>/dev/null
Press '.' to close
You pressed 's'
-----
-----
-----
111--
212--
Press '.' to close
You pressed 's'
-----
-----
-----
1-1--
222--
課題F: 消滅生成が連鎖する¶
さて、最後に、「消滅生成によってあらたに発生した数字も、連鎖的に周りと反応する」ようにしましょう。 つまり、以下の盤面において、
-----
-----
-----
-1---
21---
-----
-----
-----
-----
22---
-----
-----
-----
-----
3----
1----
-----
-----
-----
3----
自動採点は以下です。
課題Fの自動採点
入力コマンド:
echo 'ssssddsddsxxx.' | ./a.out 2>/dev/null
Press '.' to close
You pressed 's'
-----
-----
-----
-1---
21---
Press '.' to close
You pressed 'x'
-----
-----
-----
-----
22---
Press '.' to close
You pressed 'x'
-----
-----
-----
-----
3----
Press '.' to close
You pressed 'x'
1----
-----
-----
-----
3----
【2023.11.237更新】「オブジェクトが積んである状況で、連鎖がおこり、下のオブジェクトがなくなったとき、上のオブジェクトは空中にとどまるのか?落ちるのか?」という意見をもらいました。このパターンは自動採点でチェックしていないので、「なんでもOK」とします。
つまり、
--1--
-----
-----
-1---
321--
の状態で1を落下させると
-----
-----
-----
-1---
33--
となったのち、さらに「33」がつながって「4」になりますが、このとき
-----
-----
-----
-----
41---
-----
-----
-----
-1---
4----
上記は以下のコマンドで再現できます。
echo 'ssssssssssddsddssdddsddsddddsxxx.' | ./a.out 2>/dev/null
week7_2¶
さて、最後に自由課題です。main.c
を編集し、好きに落ちものゲームを作ってください。
week7_1をベースにしてもいいですし、全く新しいものを作ってもいいです。
完成したら、README.md
も編集してください。README.md
に解説がちゃんと書いていない場合は、減点するかもしれません。
オリジナルのスイカゲーム近付けるような改造でもいいですし、全然違う別ものにしてもいいです。例えば:
- 発生する数字を1だけではなくランダムにする
- 数字が大きくなると、オブジェクトも大きくなる
- 物理エンジンっぽく、オブジェクトが当たると跳ねたりするようにする
- ゲームオーバーをちゃんと実装する
- ネクストアイテムの表示
- 画面をリッチにする
- 対戦機能を実装する
優秀作品はソフ1メンバーに共有します。
諸注意¶
別のソースファイル(xxx.cやyyy.h)やテキストファイルを作って配置してもいいです。巨大なバイナリをアップロードすることは控えてください。
ライブラリを追加で入れることはOKですが、Google Cloud Shell Editorで実行できることを前提にしてください。例えばライブラリをapt
で入れる場合は、以下のようなものをREADMEに書いておいてください。
まずXXXのライブラリを以下のようにインストールする
$ sudo apt install XXX
コンパイルするときは以下のようにXXXのフラグをつける
$ gcc main.c -o main -lXXX
注意として、GUIを扱うものは、Google Cloud Shell Editorで再現できない場合は採点しません。すなわち、OpenCV, OpenGL, DirectXなどで、GUIウインドウが出るものは、再現できません。
OpenGLなどを使えばいくらでもカッコいいものは出来ると思うのですが、今回はターミナルを使うc言語入門の講義なので、ターミナルベースで動くものでイケてるゲームを追求する方向でいきましょう。
課題はC言語で行ってください。C++はダメです。本講義ではローレベルなポインタを扱うことが主題の一つです。C++を使うと簡単にポインタを隠蔽できるので、C++の使用は無しとます。
繰り返しになりますが、ウェブサイトや書籍などを参考にした場合はその出典を書いておいてください。
チーム¶
【2023.11.17更新】チーム決めは11/24 23:59までに行ってください
また、week7_2はチームでチャレンジしてもいいです。その場合、
- メンバーを集める
- メンバー全員がweek7_1の課題AからFまで終わっていることを確認
- 代表者を決める。
- メンバー全員+松井が参加するslack DMを作る
- そのDM上で、松井まで以下をDMする
- メンバー全員の名前
- メンバー全員のGitHubアカウント
- 代表者が誰か
- 代表者のweek7_2リポジトリを更新していく。(代表者以外はweek7_2リポを作らなくて大丈夫です)
をお願いします。すると、「代表リポジトリ」に対するアクセス権限を他のメンバーに与えます。あとは、今日ならったチームコーディングなどを用いて、一つのリポジトリを みんなで更新していってください。
チーム人数の目安は3人程度です。最大で5人とさせてください。また、READMEには各メンバーが主にどの部分を分担して作ったのか書いておいてください。
ちなみに、誰がどこをコーディングしたのかはgitのコミット履歴からもチェック出来ます。なので、ちゃんとコードを書いた本人がリポジトリを更新してください。「更新係を作ってその人のみが更新する」というようなことはやめてください(それをされると、松井から見た場合、更新係以外はサボっているように見えてしまいます)