C言語でじゃんけんゲーム

20170429023223
 

 
 
以前 C#RubyOOP を使ったじゃんけんゲームを作ってみましたが、同等の機能をもったそれを C言語のお勉強として作ってみました。C言語だから手続き型プログラミングということになりますが、C 初心者のためひどいコードでしょう。なるたけ改良してみたいと思っています。
 
Linux Mint 18 で確認しました。コンパイラgcc です。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

struct {
    char *name;
    int  wincount;
} pc, user;


char* show_hand(int hd_num) {
    char *hd_name[] = {"グー", "チョキ", "パー"};
    return hd_name[hd_num - 1];
}

void judge(int cp_hand, int hand) {
    printf("%s 対 %s で ", show_hand(cp_hand), show_hand(hand));
    if (cp_hand == hand) {
        printf("引き分けです。\n");
        return;
    }
    if ((3 + cp_hand - hand) % 3 == 2) {
        printf("%s", pc.name);
        pc.wincount++;
    } else {
        printf("%s", user.name);
        user.wincount++;
    }
    printf("の勝ちです。\n");
}

void game(int n) {
    char a[20];
    int  hand, cp_hand;
    
    printf("*** %d回戦 ***\n", n);
    
    do {
        printf("%sの手を入力して下さい(1:グー, 2:チョキ, 3:パー):", user.name);
        scanf( "%s", a);
        if (!strcmp(a, "1") || !strcmp(a, "2") || !strcmp(a, "3")) hand = atoi(a);
        else hand = 0;
    } while (!hand);
    cp_hand = rand() % 3 + 1;
    
    judge(cp_hand, hand);
}

void final_winner() {
    printf("\n*** 最終結果 ***\n");
    printf("%d%d で ", pc.wincount, user.wincount);
    if (pc.wincount == user.wincount) {
        printf("引き分けです。\n");
        return;
    }
    if (pc.wincount > user.wincount) printf("%s", pc.name);
    else printf("%s", user.name);
    printf("の勝ちです。\n");
}

int main() {
    srand((unsigned)time(NULL));    /* 乱数のシードを time で与える */
    
    pc.wincount   = 0;
    user.wincount = 0;
    
    pc.name = "Computer";
    
    char st[30];
    printf("あなたの名前を入力して下さい:");
    scanf("%s", st);
    user.name = st;
    printf("%s 対 %s :じゃんけん開始\n\n", pc.name, user.name);
    
    for (int n = 1; n <= 5; n++) game(n);
    
    final_winner();
    
    return 0;
}

Linux のマルチブートについて(Ubuntu 17.04)

GParted の画像を見て下さい。


上の sda が本体の PC で、下の sdb は外付けHDD です。全部で 6つの Linux ディストリビューションがインストールされているのがわかるかと思います。

sdb3 の Kubuntuいらない子なので、ここに Ubuntu 17.04 を入れてみようと思います。

まず、ubuntu-17.04-desktop-amd64.iso を Ubuntu の公式サイトからダウンロードして下さい。
それを Brasero などで DVD-ROM にイメージとして焼いて(ファイルのコピーではありません)インストールするのがふつうですが、僕の中古PC は DVDドライブの調子が悪いので、USBメモリにディスクイメージを焼きました。

$ su
# cat ubuntu-17.04-desktop-amd64.iso > /dev/sdX
# sync

sdX の X には実際の USBメモリのデバイス名を入れて下さい。ここを間違えると簡単にシステムがぶっ壊れますので、慎重に。なお、USBメモリ内のすべてのデータが上書きされて失われますので、気をつけて下さい。

PC を再起動し、USBメモリをブートデバイスに選択して(自分の PC では PC 起動時に [F12] キーで起動デバイスが選択できます)インストールを開始します。USBメモリからのブートはどの PC でも可能なわけではないようです。無理な場合はふつうに DVD-ROM からブートして下さい。
 
 

インストールの言語に [日本語] を選びます。
 

[それ以外] を選びます。マルチブートはこれでやる必要があります。
 

今回は /dev/sdb3 にインストールします。
 

[利用方法] は ext4 を、[マウントポイント] は「/」を選択します。[初期化] はどちらでもよいでしょう。
 

ブートローダをどこに入れるかですが、HDD の先頭に入れればこの OS がデフォルトのそれになります。ここでは外付けHDD に GRUB をいれたくないので、/dev/sdb3 に入れることにします。ただし、これを選択するとそのままではインストールしたこの OS が GRUB の起動メニューに現れないので、あとでフォローが必要になります。それで取り敢えず [インストール] を押せばあとはさほど問題ないでしょう(すべてデフォルトのままでいけると思います)。
 
 
さて、インストールが終了して PC を再起動させますが、このままでは入れた Ubuntu 17.04 が GRUB の起動メニューにありません。なので他のインストール済の OS から Grub Customizer を立ち上げます。Grub Customizer が入っていなければ、

$ sudo add-apt-repository ppa:danielrichter2007/grub-customizer
$ sudo apt-get update && sudo apt-get install grub-customizer

で入れます。

このように、インストールした Ubuntu 17.04 がメニューに出ているので、[保存] を押せば修正おわりです。
 
あとはもう一度再起動してみて、きちんと Ubuntu 17.04 が立ち上がるか確認して下さい。お疲れ様でした。
 
 
なお、以上はレガシーBIOS の場合です。最近の UEFI BIOS の場合はこれとは多少ちがうので注意して下さい。

迷路の中を歩く(Ruby)

ひとつ前の記事で迷路を生成したので、その中を歩いてみるプログラムを書きました。OpenGL を使っています。


 
 
赤い床のマスがゴールです。"v" で左回転、"b" で右回転、スペースで前進します。迷路は実行のたびに新たに生成されます。
ぐぐってみても意外とこんな単純なゲーム(?)の実装がないのですよねえ。

require './miniopengl'
require './maze'

module WalkMaze
  def self.start(width, height, yokokabe, tatekabe)
    MiniOpenGL.app width: 400, height: 400, depth_buffer: :on, title: "maze" do
      px = width - 1; pz = height - 1    #迷路のスタート位置
      dir = [0, -1]                      #最初の向きの設定
      
      draw do
        glEnable(GL_BLEND)         #混合処理を可能にします
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        self.color_alpha = 0.9 
        glEnable(GL_DEPTH_TEST)    #隠面消去
        clear
        
        color(0, 1, 1)
        (height + 1).times {|z| line3(0, 0, z, width, 0, z)}
        (width  + 1).times {|x| line3(x, 0, 0, x, 0, height)}
        
        color(0, 0.8, 0)
        [height + 1, width].nest_loop do |z, x|
          next if yokokabe[x + z * width].zero?
          vtx = [x, 0, z, x + 1, 0, z, x + 1, 1, z, x, 1, z]
          draw_vertex(GL_QUADS, 3, vtx)
        end
        [width + 1, height].nest_loop do |x, z|
          next if tatekabe[z + x * height].zero?
          vtx = [x, 0, z, x, 0, z + 1, x, 1, z + 1, x, 1, z]
          draw_vertex(GL_QUADS, 3, vtx)
        end
        
        color(1, 0, 0)
        draw_vertex(GL_QUADS, 3, [0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1])    #ゴールの床
        
        display
      end
      
      reshape do |w, h|
        viewport(0, 0, w, h)
        init_projection
        perspective(120, w / h.to_f, 0.2, 20)
        init_modelview
        look_at(x = px + 0.5, 0.5, z = pz + 0.5, x + dir[0], 0.5, z + dir[1], 0, 1, 0)
      end
      
      check = lambda do
        if dir[0].zero?
          z = (dir[1] < 0) ? pz : pz + 1
          f = yokokabe[z * width + px]
        else
          x = (dir[0] < 0) ? px : px + 1
          f = tatekabe[x * height + pz]
        end
        f.zero?
      end
      
      end_f = false
      
      key_in do |key|
        exit if end_f
        case key
        when "v"    #左
          dir = [dir[1], -dir[0]]
        when "b"    #右
          dir = [-dir[1], dir[0]]
        when " "    #前
          if check.call    #前へ進めるかどうか
            px += dir[0]
            pz += dir[1]
          end
        end
        init_modelview
        look_at(x = px + 0.5, 0.5, z = pz + 0.5, x + dir[0], 0.5, z + dir[1], 0, 1, 0)
        redisplay
        if px.zero? and pz.zero?
          puts "Goal!"
          end_f = true
        end
      end
    end
  end
end


Width = 10; Height = 10

yokokabe, tatekabe = Maze.new(Width, Height).generate

Left = 20; Top = 20
CellWidth = 20

#fork { show_maze(Width, Height, yokokabe, tatekabe) }

WalkMaze.start(Width, Height, yokokabe, tatekabe)

maze.rb はひとつ前の記事です。miniopengl.rb はこちらを参照。

なお、LinuxOpenGL についてはこちらも参照。

Ruby で迷路作成

http://apollon.issp.u-tokyo.ac.jp/~watanabe/tips/maze.html
ここのリンク先のアルゴリズムを使って、迷路のジェネレーターを Ruby で書いてみました。リンク先でも Ruby での実装がありますが、自分でやってみました。


20×20の迷路です。

 

コードは以下です。迷路の作成と描画は別にしてあります。描画は自作の Gem 'oekaki' を使っています。
oekaki | RubyGems.org | your community gem host
GTK+でお絵かきしてみた(Ruby) - Camera Obscura
また、自作のライブラリ 'utils' も使っています(参照)。メソッド Array.make と nest_loop がそれです。
maze.rb

require 'oekaki'
require 'utils'

class Maze
  def initialize(width, height)
    @width = width
    @height = height
    
    @yokokabe = Array.new(@width  * (@height + 1), 1)
    @tatekabe = Array.new(@height * (@width  + 1), 1)
    
    @left_wall = (@width..((a = @yokokabe.size) - @width - 1)).to_a
    @left_wall += ((a + @height)..(a + @tatekabe.size - @height - 1)).to_a
    @yokokabe_num = @yokokabe.size
    
    @cells = Array.make([@width, @height])
    #@cell にすべてちがう値の数を与える
    i = 0
    [@height, @width].nest_loop do |y, x|
      @cells[x][y] = i
      i += 1
    end
  end
  
  def generate
    break_wall until finish
    [@yokokabe, @tatekabe]
  end
  
  def wall_position(num)
    po = []
    po[0] = (num < @yokokabe_num)    #横壁なら true、縦壁なら false
    if po[0]
      a = @width
    else
      num -= @yokokabe_num
      a = @height
    end
    po[1] = num % a
    po[2] = num / a
    po
  end
  
  def replace(s, t)
    [@width, @height].nest_loop do |x, y|
      @cells[x][y] = t if @cells[x][y] == s
    end
  end
  
  def break_wall
    num = @left_wall[rand(@left_wall.size)]    #残された壁から壊す壁をランダムに選択する
    po = wall_position(num)                    #壊す壁の情報を得る
    x = po[1]; y = po[2] - 1
    @left_wall.delete(num)
    if po[0]
      #横壁
      return if (a = @cells[x][y]) == (b = @cells[x][y + 1])
      @yokokabe[num] = 0                       #実際に壁を壊す
    else
      #縦壁
      return if (a = @cells[y][x]) == (b = @cells[y + 1][x])
      @tatekabe[num - @yokokabe_num] = 0       #実際に壁を壊す
    end
    a, b = b, a if a < b
    replace(a, b)                              #壁を壊したあとに通路をつなげる
  end
  
  def finish    #@cell の値がすべて等しくなれば終了
    a = @cells[0][0]
    @cells.flatten.each {|b| return false if b != a}
    true
  end
end


def show_maze(w, h, yokokabe, tatekabe)
  wi = Left * 2 + w * (CellWidth + 1)
  he = Top  * 2 + h * (CellWidth + 1)
  
  Oekaki.app width: wi, height: he, title: "maze" do
    draw do
      color(0, 0, 0)
      rectangle(true, 0, 0, wi, he)
      
      color(65535, 65535, 65535)
      [h + 1, w].nest_loop do |y, x|
        next if yokokabe[x + y * w].zero?
        x1 = Left + x * (CellWidth + 1)
        y1 = Top  + y * (CellWidth + 1)
        line(x1, y1, x1 + CellWidth + 1, y1)
      end
      [w + 1, h].nest_loop do |x, y|
        next if tatekabe[y + x * h].zero?
        x1 = Left + x * (CellWidth + 1)
        y1 = Top  + y * (CellWidth + 1)
        line(x1, y1, x1, y1 + CellWidth + 1)
      end
      
      #save_pic(get_pic(0, 0, wi, he), "maze.png")    #画像ファイルの作成
    end
  end
end


if __FILE__ == $0
  Width = 20; Height = 20
  
  yokokabe, tatekabe = Maze.new(Width, Height).generate
  
  Left = 20; Top = 20
  CellWidth = 20
  
  show_maze(Width, Height, yokokabe, tatekabe)
end

メソッド Maze#generate で迷路を生成します。返り値は yokokabe と tatekabe で、それぞれ横方向と縦方向の壁をあらわす配列です(1 なら壁が存在し、0 なら存在しない。初期値はすべて 1)。メソッド Maze#break_wall は、壁をランダムにひとつ選んで、壊してもよい壁なら壊します。メソッド show_maze で迷路を表示します。ここで自家製の Gem 'oekaki' を使っています。png 画像ファイルに落とすことも出来ます。

Utils

上のコードに必要な 'utils' の部分は以下だけなので、これで代用してもらって構いません。
utils.rb

class Array
  #任意の階層だけ繰り返しをネストする
  def nest_loop(arg = [], &bk)
    check = lambda do |obj|
      return 0...obj if (c = obj.class) == Bignum or c == Integer or c == Fixnum
      obj
    end
    
    ar = dup
    for i in check.call(ar.shift)
      if ar.empty?
        yield(arg + [i])
      else
        ar.nest_loop(arg.push(i), &bk)
        arg.pop
      end
    end
    self
  end

  #多重配列を簡単に生成する
  def self.make(ar, ob=nil)
    raise "Argument class Error" unless ar.class == Array
    a = ar.dup
    ar1 = []
    a.shift.times {ar1 << (a.empty? ? (ob.dup rescue ob) : Array.make(a, ob))}
    ar1
  end
end

 

※追記
これで作った迷路を実際に歩いてみるプログラムを書きました。

Linux Mint(Ubuntu)でSDカードに書き込みできない

まず SDカードを PC に挿入しても認識しない。
これは何故か SDカードを LOCK してから挿入したら認識した。

もう一度 LOCK を解除して挿入。しかしフォルダをコピーしても「転送先は読み込み専用です」という表示が出る。アンマウントして再マウントしてもダメ。パーミッションを変更してもダメ(というか、Linuxファイルシステムでないので無意味)。ちなみにファイル・システムは msdos
よくわからずにダメ元で SDカードを挿入したままで再起動。そうしたら普通に認識された。何なんだ。

「等値」と「等価」は Ruby では?

これを見てどうも JavaC++ の話のように思ったのだが、最初の「『等値』と『等価』の違いを説明してください」というのがよくわからなかった。僕は Ruby しか知らない素人初心者プログラマなのだが、Ruby だとどういうことなのだろう。

ただ、Ruby でも「==」と「equal?」はちがうよね。

irb(main):001:0> a = "hoge"
=> "hoge"
irb(main):002:0> b = "hoge"
=> "hoge"
irb(main):003:0> a == b
=> true
irb(main):004:0> a.equal?(b)
=> false
irb(main):005:0> a.object_id
=> 47028457345780
irb(main):006:0> b.object_id
=> 47028458255160
irb(main):007:0> a = b = "hoge"
=> "hoge"
irb(main):008:0> a.equal?(b)
=> true

上を見ればわかると思うけれども、「==」はオブジェクトが同じ内容なら true、「equal?」は同じオブジェクトID でなければ true にならない。上の「等値」と「等価」というのは、これと関係があるのかな。

Ruby で関数型プログラミングもどき

上のリンク先で JavaScript を使って、オブジェクト指向プログラミングと関数型プログラミングの対比をやってあったので、Ruby に移植してみました。

 

課題:
唐揚げ弁当がいくつかあるとします。それぞれ唐揚げが複数入っています。
この中からx個の唐揚げをつまみ食いするプログラムを作りましょう。
つまみ食いはバレないようにするために、
その時点で最も唐揚げ数が多いお弁当から取るという仕様にします。

http://qiita.com/stkdev/items/5c021d4e5d54d56b927c

 

オブジェクト指向で。こんな感じですかね。

class Bento
  def initialize(dish, num)
    @dish = dish
    @num = num
  end
  attr_reader :num
  
  def eat
    @num -= 1
  end
  
  def show
    puts @dish + @num.to_s + ""
  end
end

order = [Bento.new("唐揚げ", 10), 
         Bento.new("唐揚げ",  8),
         Bento.new("唐揚げ",  6)]
         
5.times do
  max_bento = order.inject(order.first) {|r, bento| r = (bento.num > r.num) ? bento : r}
  max_bento.eat
  puts "-------"
  order.each {|x| x.show}
end

結果。

-------
唐揚げ9個
唐揚げ8個
唐揚げ6個
-------
唐揚げ8個
唐揚げ8個
唐揚げ6個
-------
唐揚げ7個
唐揚げ8個
唐揚げ6個
-------
唐揚げ7個
唐揚げ7個
唐揚げ6個
-------
唐揚げ6個
唐揚げ7個
唐揚げ6個

 
 
次は関数型プログラミングっぽく。元の JavaScript コードは上と同じ答えを返さないので、多少変更してあります。

order = [{dish: "唐揚げ", num: 10},
         {dish: "唐揚げ", num:  8},
         {dish: "唐揚げ", num:  6}]
         
show = ->(d) {puts d[:dish] + d[:num].to_s + ""}

show_all = ->(data) {data.each {|x| show[x]}}

select_eating_bento = ->(data) {
  data.inject(data.first) {|r, bento| r = (bento[:num] > r[:num]) ? bento : r}
}

eating_karaage = ->(data) {
  data.map do |bento|
    if bento.equal?(select_eating_bento[data])
      {dish: bento[:dish], num: (bento[:num] - 1)}
    else
      bento
    end
  end
}

eating = ->(num, data) {
  data = eating_karaage[data]
  puts "-------"
  show_all[data]
  eating[num - 1, data] if num > 1
}

eating[5, order]

「副作用」がないようにというのが目標ですね。
 
どちらがわかりやすいでしょうか。

元記事は色いろコメントで怒られていますね…。