September 13, 2011

Google Developer Day 2011:DevQuiz解答晒し大会(7)

スライドパズル

結果はというと……364/5000。いまひとつうまくいかなかった感じ。

  • 基本は幅優先探索
  • 評価値の小さいものを優先的に探索
  • ある程度展開した時点で評価値の小さい64組以外をカット(3*3を除く)
  • 評価値 = (パネルのマンハッタン距離の和)+(異なるパネル数)

A*探索も実装してはみたが投入まではいかなかった。

コードはrubyで実装。Pentium4 2.4GHz、メモリ4GBのマシンで実行した。マス数9~16の状態で一通りまわして300問、マス数18~25は1000問くらいの時点で打ち切って64問という具合。効率を一切考えずに書いたんで課題は多いかな。

プログラムは3つに分割した。

1. solver/mod_bfsearch.rb


load 'solver/util.rb'

class Solver

  include SolverUtil

  attr :board
  attr :width
  attr :height
  attr :result

  def initialize(b,w,h)
    @board=b
    @width=w
    @height=h
    r="123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[0..b.length-2] + '0'
    (0..b.length-1).each { |pos|
      r[pos]='=' if b[pos]=='='
    }
    @result=r
  end

  def solve(start=@board,close=[])
    queue=[]
    board=start
    init_score = major(@board)
    move = ''
    until (board == @result || move ==  nil)
      available(board,@width,@height).split('').delete_if { |c|
        after = move(c,board,@width,@height)
        close.include? after
      }.map { |c|
        after = move(c,board,@width,@height)
        score = major(after)
        queue.push [move+c, after, score] if (init_score > score || @width*@height < 10)
        close.push after
      }
      if (queue.length > 512)
        queue.sort! { |a, b| a[2] <=> b[2] }
        if (@width*@height > 10)
          new_queue = queue[0..63]
          queue = new_queue
          # init_score = (init_score * 9) / 10
          init_score = queue[63][2]
        end
      end
      (move, board, s) = queue.shift
    end
    move
  end

  def major(board)
    dist=0
    (0..board.length-1).each { |n|
      if (board[n] != '=')
        m = @result.index(board[n])
        dist += dist(m,n,@width)
      end
    }
    dist + diffs(board)
  end

  def dist(m,n,w)
    d = abs(m-n)/w
    if (m > n)
      m -= d*w
    else
      n -= d*w
    end
    d+abs(m-n)
  end

  def abs(x)
    x = -x if x < 0 
    x
  end

  def diffs(board)
    d = 0
    (0..board.length-1).each { |n| 
      d += 1 if board[n] != @result[n] && board[n] != '0'
    }
    d
  end
end

2. solver/util.rb


module SolverUtil

  def available(board,width,height)
    result=''
    pos = board.index('0')
    result += 'L' if (pos%width != 0 && board[pos-1] != '=')
    result += 'R' if (pos%width != width-1 && board[pos+1] != '=')
    result += 'U' if (pos/width != 0 && board[pos-width] != '=')
    result += 'D' if (pos/width != height-1 && board[pos+width] != '=')
    result
  end

  def move(c,board,width,height)
    case c
    when 'L'
      result=left(board,width,height)
    when 'R'
      result=right(board,width,height)
    when 'U'
      result=up(board,width,height)
    when 'D'
      result=down(board,width,height)
    end
    result
  end

  def left(board,width,height)
    result=board.dup
    pos=board.index('0')
    if (pos%width != 0 && board[pos-1] != '=')
      result[pos]=result[pos-1]
      result[pos-1]='0'
    end
    result
  end

  def right(board,width,height)
    result = board.dup
    pos = board.index('0')
    if (pos%width != width-1 && board[pos+1] != '=')
      result[pos]=result[pos+1]
      result[pos+1]='0'
    end
    result
  end

  def up(board,width,height)
    result = board.dup
    pos = board.index('0')
    if (pos/width != 0 && board[pos-width] != '=')
      result[pos]=result[pos-width]
      result[pos-width]='0'
    end
    result
  end

  def down(board,width,height)
    result = board.dup
    pos = board.index('0')
    if (pos/width != height-1 && board[pos+width] != '=')
      result[pos]=result[pos+width]
      result[pos+width]='0'
    end
    result
  end

end

3. main.rb


load 'solver/mod_bfsearch.rb'

def consume_count(s,c)
  r=0
  s.split('').each { |c1|
    r += 1 if c1 == c
  }
  r
end

result_file = ARGV[0]
min_size = ARGV[1].to_i
max_size = ARGV[2].to_i

result = open(result_file,"wb")

open("problems.txt", "r") do |f|
  move_limits = f.gets.split(' ').map{ |n| n.to_i }
  num_problems = f.gets.to_i
  num = 0
  solved = 0
  while (f.gets)
    w,h,b=$_.chomp.split(',')
    w = w.to_i
    h = h.to_i
    STDERR.puts "#{num+1} #{solved} #{b} #{w} #{h} #{move_limits}"
    if (w.to_i * h.to_i > max_size || w.to_i * h.to_i < min_size)
      result.puts ''
    else
      board=Solver.new(b,w.to_i,h.to_i)
      s=''
      begin
        s = board.solve
        if (s != nil)
          move_consume = ['L','R','U','D'].map { |c| consume_count(s,c) }
          (0..3).each { |n|
            move_limits[n] -= move_consume[n]
          }
          STDERR.puts "#{s} #{move_consume}"
          result.puts s
          solved += 1
        else
          result.puts ''
        end
      rescue SystemStackError
        result.puts ''
      end 
    end
    num += 1
  end
end

Google Developer Day 2011:DevQuiz解答晒し大会(6)

分野別問題(5) 一人ゲーム

当初、変なヒューリスティクスを入れたコードで2度ほど失敗。最後は簡単な方針でコードを作成して解いた。まずは擬似コード

procedure 一人ゲーム(q)
  if (qが空である) return 0
  if (∀n[n∈q, nが5の倍数]) return 1
  count ← 0
  while (∃n[n∈q, nが5の倍数])
    q ← div2(q)
    count ← count+1
  end
  return count+1+min(一人ゲーム(elim5(q)), 一人ゲーム(div2(q)))
end

実際のコード(rubyで書いた)

module Solver

  module_function

  def solve(question)

    count = 0

    if (question.length == 0)
      return 0
    end

    if (only_fives?(question))
      return 1
    end

    q = question.dup

    while (not_exists_fives?(q))
        q.map! { |n| n/2 } 
        count = count + 1
    end

    q1 = q.dup.delete_if { |n| n%5 == 0 }
    count1 = count + 1 + solve(q1)
    q.map! { |n| n/2 }
    count2 = count + 1 + solve(q)

    (count1<count2)?count1:count2

  end

  def not_exists_fives?(question)
    question.each { |n| 
      return false if (n%5 == 0)
    }
    return true
  end

  def only_fives?(question)
    question.each { |n|
      return false if (n%5 != 0)
    }
    return true
  end

end

open("question.txt", "r") do |f|
  number = f.gets.to_i
  count = 0
  while (count < number)
    size = f.gets.to_i
    question = f.gets.chomp.split(' ').map { |n| n.to_i }
    if (size == question.size) then
      puts Solver.solve(question)
    else
      puts 0
    end
    count = count + 1
  end
end

スライドパズルは項を改めて。

Google Developer Day 2011:DevQuiz解答晒し大会(5)

分野別問題(4) Google Apps Script

APIのドキュメントを見ながら書いてみたスクリプトがこんな感じ。

function myFunction() {
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet()
  var response = UrlFetchApp.fetch("http://gdd-2011-quiz-japan.appspot.com/apps_script/data?param=8020112495504250004");
  var data_string = response.getContentText();
  var data = Utilities.jsonParse(data_string);
  for (var i = 0; i < data.length; i++) {
    var city = data[i];
    var sheet = spreadsheet.insertSheet();
    sheet.setName(city["city_name"]);
    var city_data = city["data"];
    for (var j = 0; j < city_data.length; j++) {
      var daily = city_data[j];
      var usage = daily["usage"];
      var capacity = daily["capacity"];
      var rate = Math.floor(usage * 10000 / capacity + 0.5) / 100;
      sheet.getRange(j+1, 2, 1, 1).setValue(usage);
      sheet.getRange(j+1, 1, 1, 1).setValue(capacity);
      sheet.getRange(j+1, 3, 1, 1).setValue(rate+"%");
    }
  }
  var sheet = spreadsheet.getSheetByName("シート1");
  if (sheet != null) {
    spreadsheet.setActiveSheet(sheet);
    spreadsheet.deleteActiveSheet();
  }
}

Google Developer Day 2011:DevQuiz解答晒し大会(4)

分野別問題(3) Android

AIDLファイルを使ってアプリケーションを作ればいいことはわかったが、bindServiceを呼び出してから実際にサービスに接続するまでに時間がかかることを知らずにNullPointerExceptionではまること多数。

アプリケーション

package com.example.devquizanswer;

import android.app.Activity;
import android.content.ServiceConnection;
import android.content.Context;
import android.content.ComponentName;
import android.content.Intent;
import android.os.IBinder;
import android.os.Bundle;
import android.os.RemoteException;
import android.widget.TextView;
import com.google.android.apps.gddquiz.IQuizService;

public class DevQuizAnswer extends Activity {
	
	public IQuizService quiz;
	public TextView tv;
	    
    private ServiceConnection serviceConnection = new ServiceConnection() {
    	public void onServiceConnected(ComponentName name, IBinder service) {
    		quiz = IQuizService.Stub.asInterface(service);
    		try {
    			String code = quiz.getCode();
    			tv.setText(code);
    		} catch (RemoteException e) {
    			// do nothing
    		}
    	}
    	
    	public void onServiceDisconnected(ComponentName name) {
    		quiz = null;
    	}
    };
    
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        tv = (TextView)findViewById(R.id.code);
        Intent intent = new Intent(IQuizService.class.getName());
        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
    }
    
    @Override
    protected void onDestroy() {
    	super.onDestroy();
    	unbindService(serviceConnection);
    }
    
}

レイアウト

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:orientation="vertical"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     >
<TextView android:text="TextView" android:id="@+id/code" android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
</LinearLayout>

Google Developer Day 2011:DevQuiz解答晒し大会(3)

分野別問題(2) Go

与えられるPNGファイルが以下の制約を持つことを利用している。

  • RGBそれぞれ256階調である
  • アルファチャンネルは固定である

Colorオブジェクトを配列に格納する形式だと色数が増えたときに使用メモリ量、速度ともに制約を受けるのでビットマップで色の出現を管理する形式にした。

package main

import (
    "fmt"
    "io"
    "strings"
    /* add more */
    pngimage "image/png"
)

func CountColor(png io.Reader) int {
    cnt := 0
    var color [256*256*4]uint64
    img, err := pngimage.Decode(png)
    if err != nil {
        return 0
    }
    border := img.Bounds()
    for n := border.Min.Y; n < border.Max.Y; n++ {
        for m := border.Min.X; m < border.Max.X; m++ {
            colr := img.At(m,n)
            r0, g0, b0, _ := colr.RGBA()
            index := (((r0 >> 8) << 16) | ((g0 >> 8) << 8) | (b0 >> 8)) >> 6
            var flag uint64 = 1 << ((b0 >> 8) & 0x3f)
            if (color[index] & flag == 0) {
                color[index] |= flag
                cnt += 1
            }
        }
    }
    return cnt

}
September 12, 2011

Google Developer Day 2011:DevQuiz解答晒し大会(2)

分野別問題(1) Web Game

24問目くらいまでは手で解いていたが、さすがにめげて、素直にChrome Extensionを拡張して解いた。

方針は、カードを順番にめくっていって、まだ出ていないカードなら色と位置を記録、すでに出てきたカードならペアになるカードをめくっていくというもの。1 passで解けるので楽といえば楽。

var openElement = function(id) {
    var element = document.getElementById(id);
    if (element == null) {
	return null;
    } else {
	var myevent = document.createEvent('MouseEvents');
	myevent.initEvent('click', false, true);
	element.dispatchEvent(myevent);
	return 'color' + element.style.backgroundColor;
    }
}

var color = {};
var i = 0;

alert('Start');

while(true) {
    var elementId = 'card' + i;
    var elementColor = openElement(elementId);
    if (elementColor == null) {
	break;
    }
    
    if (color[elementColor] == undefined) {  // 1枚目が未知のカード
	color[elementColor] = elementId;
	i = i + 1;
	elementId = 'card' + i;
	elementColor = openElement(elementId);
	if (color[elementColor] == undefined) { // 2枚目が未知のカード
	    color[elementColor] = elementId;
	} else { // 2枚目が既知のカード
	    var elementId1 = color[elementColor];
	    openElement(elementId);
	    openElement(elementId1);
	}
    } else { // 1枚目が既知のカード
	elementId = color[elementColor];
	openElement(elementId);
    }
    i = i + 1;
}
alert('Solved!!!');

Google Developer Day 2011:DevQuiz解答晒し大会(1)

今年もDevQuizに挑戦してみた。得点には関係ないけど分野別問題を全問解いたのでそっちも含めて書いてみる。

ウォームアップクイズはランダムに5問問題が与えられてたみたい。僕の問題は以下の5問。

  • Google Chrome
    出題されたタグを含むHTML5文書を書いてChromeで読み込ませ、Developer ToolsでDOM Objectをチェック。 
  • Google Apps Script
    当てずっぽうその1。ほぼ総当り。
  • maps API
    maps APIのドキュメントとソースコードを眺めて一番それらしい選択肢を選択。 
  • Android
    当てずっぽうその2。1回間違い。 
  • Google translate
    日本語→ロシア語翻訳しました(邪道…)

分野別問題は項を改めて。

February 26, 2011

「ネット・バカ」を読んでいる

Nicholus Carrの最新の著書。翻訳のタイトルで相当損をしている(原著タイトルは “THE SHALLOWS”)。あながち間違ってもないんだけど誤解を招く、というか。

February 15, 2011

久しぶりに何かを作ろうと思い立って

適当な題材がないかな、と思ってはじめたのが15puzzle。とりあえずHTML+Javascriptで作ろうと考えている。

はじめの1歩ということで、ここまで実装してみた。

  1. tableで15puzzleのボードを表示する。
  2. マウスでパネルをクリックすると、クリックしたパネルの位置を下に表示する。

コードを書く習慣が久しくなかったのでやっぱり時間がかかった。とりあえずこんな感じ。

HTML

<!DOCTYPE html>
<html>
<head>
  <title>15 puzzle(table version)</title>
  <link rel="stylesheet" type="text/css" href="15puzzle.css" />
  <script type="text/javascript" src="15puzzle.js"></script>
</head>
<body>
<table id="board">
  <tr>
     <td class="cell" id="cell_0">1</td>
     <td class="cell" id="cell_1">2</td>
     <td class="cell" id="cell_2">3</td>
     <td class="cell" id="cell_3">4</td>
  </tr>
  <tr>
     <td class="cell" id="cell_4">5</td>
     <td class="cell" id="cell_5">6</td>
     <td class="cell" id="cell_6">7</td>
     <td class="cell" id="cell_7">8</td>
  </tr>
  <tr>
     <td class="cell" id="cell_8">9</td>
     <td class="cell" id="cell_9">10</td>
     <td class="cell" id="cell_10">11</td>
     <td class="cell" id="cell_11">12</td>
  </tr>
  <tr>
     <td class="cell" id="cell_12">13</td>
     <td class="cell" id="cell_13">14</td>
     <td class="cell" id="cell_14">15</td>
     <td class="cell" id="cell_15"></td>
  </tr>
</table>

<p id="click"></p>

</body>
</html>

CSS

#board {
    border: solid 3px black;
}

td.cell {
    border: solid 3px black;
    text-align: center;
    width: 2em;
    height: 2em;
}

Javascript

window.onload = function() {
    var board = document.getElementById("board");
    var cells = board.getElementsByTagName("td");
    var click = document.getElementById("click");

    for (i = 0; i < cells.length; i++) {
	cells[i].onclick = function() {
	    var self = this;
	    click.innerText = self.id;
	};
    }
};
August 1, 2009

GMC-4で簡単なコードを書いてみた

小学生時代以来なんで26年ぶりくらいか(笑)。簡単に説明するとこんな感じ。

  1. 実行すると7セグメントLEDに「b」を表示
  2. 2進LEDを6→5→4→3→2→1→0の順で点灯→消灯
  3. 2.の途中で4に到達した時点で7セグメントLEDの表示を「d」に変更

投擲をイメージしてみたもの。どうですかね。

00        TIA B        8 B
02        AO           1
03        TIY 6        A 6
05 L1    CAL SETR   E 1
07        TIA A        8 0
09        CAL TIMR   E C
0B        CAL RSTR   E 2
0D        CIY 0        D 0
0F        JUMP L3    F 1 5
12 L2    JUMP L2    F 1 2
15 L3    AIY F        B F
17        CIY 4        D 4
19        JUMP L1    F 0 5
1C        TIA D        8 D
1E        AO           1
1F        JUMP L1    F 0 5