challenge あみだくじ

次のような書式で与えられた「あみだくじ」があります。
(あみだくじはコード中に埋め込んでも、標準入力や
外部ファイルから読み込んでも、書きやすい方法でかまいません)

A B C D E
| | |-| |
|-| | |-|
| |-| |-|
|-| |-| |
|-| | | |

このあみだくじをたどって
A B C D E
| | |-| |
|-| | |-|
| |-| |-|
|-| |-| |
|-| | | |
B D C A E
のように結果を表示させるプログラムを作ってください。

Posted feedbacks - Nested

Flatten Hidden

なんの捻りもなく。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
static const char* AMIDA[] = {
    "A B C D E",
    "| | |-| |",
    "|-| | |-|",
    "| |-| |-|",
    "|-| |-| |",
    "|-| | | |",
    NULL,
};

int main(int argc, _TCHAR* argv[])
{
    char szAns[100];
    strcpy(szAns, AMIDA[0]);
    printf("%s\n", AMIDA[0]);

    for (int n = 1; AMIDA[n] != NULL; n++)
    {
        printf("%s\n", AMIDA[n]);

        for (int p = 0; AMIDA[n][p] != '\0'; p++)
        {
            if ((AMIDA[n][p] == '-') && (p > 0))
            {
                char c = szAns[p - 1];
                szAns[p - 1] = szAns[p + 1];
                szAns[p + 1] = c;
            }
        }
    }

    printf("%s\n", szAns);
    return 0;
}

普通に。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import sys

def main():
    if not len(sys.argv) == 2:
        sys.stderr.write("usage: input_file_name\n")
        return
    a = None
    for line in (line.rstrip() for line in open(sys.argv[1])):
        print line
        if a is None: # first line
            a = list(line)
        else: # after second line
            for i in xrange(1, len(a), 2):
                if line[i] == '-':
                    a[i - 1], a[i + 1] = a[i + 1], a[i - 1]
    print "".join(a)

if __name__ == '__main__':
    main()

素直に置換してみました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Answer103 {
    public static List<String> solveAmida(String[] input) {
        List<String> symbols = Arrays.asList(input[0].split("\\s+"));
        List<String> result = new ArrayList<String>(symbols);
        for (String line: input) {
            String[] separators = line.split("\\|");
            for (int index = 0; index < separators.length; index++) {
                if (separators[index].startsWith("-")) {
                    result.add(index - 1, result.remove(index));
                }
            }
        }
        return result;
    }

    public static void main(String[] args) {
        String[] amida = new String[] {
                        "A B C D E",
                        "| | |-| |",
                        "|-| | |-|",
                        "| |-| |-|",
                        "|-| |-| |",
                        "|-| | | |",
                };
        for (String line: amida) {
            System.out.println(line);
        }
        List<String> result = solveAmida(amida);
        for (String str: result) {
            System.out.print(str);
            System.out.print(" ");
        }
        System.out.println();
    }
}
Squeak Smalltalk で。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
| amida result |
amida := 'A B C D E
| | |-| |
|-| | |-|
| |-| |-|
|-| |-| |
|-| | | |'.

result := nil.
amida linesDo: [:line |
    result ifNil: [result := line subStrings] ifNotNil: [
        (2 to: line size by: 2) do: [:idx |
            (line at: idx) = $- ifTrue: [result swap: idx // 2 with: idx // 2 + 1]]]].
^result   "=> #('B' 'D' 'C' 'A' 'E') "

とりあえず。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
val s = [
"A B C D E",
"| | |-| |",
"|-| | |-|",
"| |-| |-|",
"|-| |-| |",
"|-| | | |"
]

fun amida [] = []
  | amida (a as (x::xs)) =
  let
    fun loop [] y = y
      | loop (s::ss) y =
      let
        fun f ((i, _), v) =
          substring (v, 0, i - 1) ^
          (implode o rev o explode o substring) (v, i - 1, 3) ^
          String.extract (v, i + 2, NONE)
      in
        loop ss (foldl f y (global_find "-" s))
      end
  in
    a @ [loop xs x]
  end

val _ = app println (amida s)
適当。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Module Module1

    Sub Main()
        Dim input As String = _
            "A B C D E" & ControlChars.CrLf & _
            "| | |-| |" & ControlChars.CrLf & _
            "|-| | |-|" & ControlChars.CrLf & _
            "| |-| |-|" & ControlChars.CrLf & _
            "|-| |-| |" & ControlChars.CrLf & _
            "|-| | | |"

        Console.WriteLine(input)
        Console.WriteLine(GhostLeg(input))
        Console.ReadKey()
    End Sub

    Public Function GhostLeg(ByVal input As String) As String
        Dim inputlines As String() = input.Split(New String() {ControlChars.CrLf}, StringSplitOptions.RemoveEmptyEntries)
        Dim lines As String() = inputlines(0).Split(Nothing)
        For i As Integer = 1 To inputlines.Length - 1
            Dim bars As String() = inputlines(i).Split(New Char() {"|"c})
            For j As Integer = 1 To bars.Length - 2
                If bars(j) <> " " Then Dim s As String = lines(j) : lines(j) = lines(j - 1) : lines(j - 1) = s
            Next
        Next
        Return String.Join(" "c, lines)
    End Function


End Module

テキストの2行目から文字でループを回し、次の文字が"-"の時に、1行目の文字を入れ替えてます。 ループが終わったら1行目を最終行に足して、入れ替えた1行目を復元して出力してます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//http://ja.doukaku.org/103/ 投稿用
using System;
using System.Collections.Generic;
class Program {
    static void Main(string[] args) {
        Amida(@"A B C D E
| | |-| |
|-| | |-|
| |-| |-|
|-| |-| |
|-| | | |");
        Console.ReadLine();
    }

    static void Amida(string amidaText) {
        string[] amidaTemp = amidaText.Split(new char[]{'\n'});
        List<char[]> amidaRows = new List<char[]>();
        amidaRows.Add(amidaTemp[0].ToCharArray());
        for(int i = 1; i < amidaTemp.Length; i++) {
            amidaRows.Add(amidaTemp[i].ToCharArray());
            for(int j = 0; j < amidaTemp[i].Length; j = j + 2) {
                if(j > 0 && amidaTemp[i][j - 1] == '-') {
                    char tmp;
                    tmp = amidaRows[0][j];
                    amidaRows[0][j] = amidaRows[0][j - 2];
                    amidaRows[0][j - 2] = tmp;
                }
            }
        }
        amidaRows.Add(amidaRows[0]);
        amidaRows.RemoveAt(0);
        amidaRows.Insert(0, amidaTemp[0].ToCharArray());

        foreach(char[] line in amidaRows) {
            Console.WriteLine(line);
        }
    }
}

最初の行の状態を1行読む毎に書き換えてます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
def amida(prob)
  prob.split("\n").inject do |r,l|
    l.split(//).each_with_index do |e,i|
      next unless e=='-'
      r[i-1..i+1]=r[i-1..i+1].reverse
    end
    r
  end
end


prob=<<-EOD
A B C D E
| | |-| |
|-| | |-|
| |-| |-|
|-| |-| |
|-| | | |
EOD

puts prob
puts amida(prob)

ありゃ、ログインしてなかった・・。

 殆ど書き直しです。エレガントに書けたと思います。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//http://ja.doukaku.org/103/ 投稿用
using System;
using System.Collections.Generic;
class Program {
    static void Main(string[] args) {
        Amida(@"A B C D E
| | |-| |
|-| | |-|
| |-| |-|
|-| |-| |
|-| | | |");
        Console.ReadLine();
    }

    static void Amida(string amidaText) {
        List<string> temp = new List<string>(amidaText.Split(new char[] { '\n' }));
        List<char> lastLine = new List<char>(temp[0].ToCharArray());
        foreach(string line in temp) {
            Console.WriteLine(line);
            for(int i = 1; i < line.Length; i = i + 2) {
                if(line[i] == '-'){
                    lastLine.Reverse(i - 1, 3);
                }
            }
        }
        Console.WriteLine(lastLine.ToArray());
    }
}

普通にArrayの中身をswapしながら各行を読んでいます.

Ruby版なかったから作り始めたのだけど,できたら,もうあったorz

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/usr/bin/ruby
amida = <<_EOD_
A B C D E
| | |-| |
|-| | |-|
| |-| |-|
|-| |-| |
|-| | | |
_EOD_

elem = nil
amida.split("\n").each do |line|
  elem.nil? and elem = line.split and next
  line.split('').grep(/-| /).each_with_index do |op, i|
    elem[i], elem[i+1] = elem[i+1], elem[i] if op == '-'
  end
end
puts amida, elem.join(' ')

__END__
% ./amida.rb
A B C D E
| | |-| |
|-| | |-|
| |-| |-|
|-| |-| |
|-| | | |
B D C A E

どうでしょう

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
s = ["A B C D E",
     "| | |-| |",
     "|-| | |-|",
     "| |-| |-|",
     "|-| |-| |",
     "|-| | | |"]

repl (x:' ':xs) ('|':y:ys) =
    let z:zs = repl xs ys in
    case y of
      '-' -> z:' ':x:zs
      _ -> x:' ':z:zs
repl xs _ = xs

main = putStrLn $ (unlines s) ++ foldl1 repl s
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
あみだ="A B C D E
| | |-| |
|-| | |-|
| |-| |-|
|-| |-| |
|-| | | |"

Amida=あみだ
AmidaFooter=Amida[0]を" "で区切る

Amidaの0を配列削除
Iで0から(Amidaの要素数)-1まで繰り返す
    AmidaLine = Amida[I]
    // 処理しやすいように整形
    AmidaLineの1から1バイト削除
    AmidaLineから1バイト右端削除
    AmidaLine = AmidaLineを"|"で区切る
    Jで0から(AmidaLineの要素数)-1まで繰り返す
        もし(AmidaLine[J]="-")ならば
            AmidaFooter = AmidaFooterのJを(J+1)に配列入替え

あみだ&改行&(AmidaFooterを" "で配列結合)を表示

●配列入替え(ArrayのAをBに)
    tmpAとは文字列=Array[A]
    tmpBとは文字列=Array[B]
    Array[A]=tmpB
    Array[B]=tmpA
    Arrayで戻る
例によって replace をイテレータ代わりに。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
function doukaku103(amida){
  var a = amida.replace(/\|/g, '').split('\n'), o = a.shift().split(' ');
  var x = /-/g, f = function(t, i){ t = o[i], o[i] = o[i+1], o[i+1] = t };
  for(var j = 0, l; l = a[j++];) l.replace(x, f);
  return amida +'\n'+ o.join(' ');
}

(typeof confirm != 'undefined' ? confirm : typeof print != 'undefined' ? print :
 function($){ typeof WSH == 'object' && WSH.echo($) })(doukaku103('\
A B C D E\n\
| | |-| |\n\
|-| | |-|\n\
| |-| |-|\n\
|-| |-| |\n\
|-| | | |'));

ひねりなし

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
use strict;
use warnings;

sub swap {
    @_[0, 1] = @_[1, 0];
}

sub amida {
    my $str = shift;
    my @lines = split /\n/, $str;
    my @header = split //, shift @lines;

    print join '', @header, "\n";

    for (@lines) {
        print $_, "\n";
        while (/-/g) {
            swap($header[pos() - 2], $header[pos]);
        }
    }

    print join '', @header, "\n";
}

my $data = do { local $/; <DATA> };
amida($data);

__DATA__
A B C D E
| | |-| |
|-| | |-|
| |-| |-|
|-| |-| |
|-| | | |
9:user> (print-result amidakuji)
A B C D E
| | |-| |
|-| | |-|
| |-| |-|
|-| |-| |
|-| | | |
B D C A E
=> #<undef>
10:user> 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
(define amidakuji
  (string-append
   "A B C D E\n"
   "| | |-| |\n"
   "|-| | |-|\n"
   "| |-| |-|\n"
   "|-| |-| |\n"
   "|-| | | |\n"
   ))

(define (first-state line)
  (map string->symbol (string-split line #\space)))

(define (update-state line prev-state)
  (let1 amida (cdr (string-split line #\|))
    (fold-right (lambda (s a k)
                  (if (string=? a "-")
                    (cons* (car k) s (cdr k))
                    (cons s k)))
                '() prev-state amida)))

(define (solve amida)
  (call-with-input-string amida
    (lambda (in)
      (port-fold
       (cut update-state <> <>)
       (first-state (read-line in))
       (cut read-line in)))))

(define (print-result amida)
  (display amida)
  (print (string-join (map symbol->string (solve amida)) " ")))

 Playerオブジェクトにあみだくじを引かせてみました。この方が"このあみだくじをたどって"という題意に近いと思います。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//http://ja.doukaku.org/103/ 投稿用
using System;
using System.Collections.Generic;
class Program2 {
    static void Main(string[] args) {
        string amidaStr = @"A B C D E
| | |-| |
|-| | |-|
| |-| |-|
|-| |-| |
|-| | | |";

        List<char[]> amida = new List<char[]>();

        //あみだをPlayerが解釈できる形式に変換
        foreach(string line in amidaStr.Split(new char[] { '\n' })) {
            amida.Add(line.ToCharArray());
        }
        amida.Add(new char[amida[0].Length]);//Playlerが回答を記入する欄

        //Player生成
        for(int i = 0; i < amida[0].Length; i = i + 2) {
            new Player(i, amida);
        }

        //出力
        foreach(char[] line in amida) {
            Console.WriteLine(line);
        }
        Console.ReadLine();
    }
}

class Player {
    public Player(int start,List<char[]> amida) {
        Play(start, 1, amida, amida[0][start]);     
    }

    private void Play(int x,int y, List<char[]> amida, char name){
        if(amida[y][x] == '|') { //あみだが終わっていなければ
            if(x >= 2 && amida[y][x - 1] == '-') {
                Play(x - 2, y + 1, amida, name); //左に移動して進む
            } else if(x <= amida[0].Length - 3 && amida[y][x + 1] == '-') {
                Play(x + 2, y + 1, amida, name); //右に移動して進む
            } else {
                Play(x, y + 1, amida, name); //移動せずに下に進む
            }
        } else {
            amida[y][x] = name; //終わったら結果を記入
        }
    }
}

全体のあみだくじの文字列をamidaに渡すと、結果の文字列を返します。

(princ

(amida

"A B C D E

| | |-| |

|-| | |-|

| |-| |-|

|-| |-| |

|-| | | |"))

;=>

A B C D E

| | |-| |

|-| | |-|

| |-| |-|

|-| |-| |

|-| | | |

B D C A E

||<

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
(require :cl-ppcre)

(defun amida (amida)
  (with-input-from-string (str amida)
    (let ((items (ppcre:split "\\s*" (read-line str nil nil))))
      (do ((line (read-line str nil :eof) (read-line str nil :eof)))
          ((eq :eof line) (format nil "~A~%~{~A~^ ~}" amida items))
        (exch line items)))))

(defun exch (pat items)
  (do ((pos (ppcre:all-matches "-" pat) (cddr pos)))
      ((endp pos) items)
    (let ((p (truncate (car pos) 2)))
      (rotatef (nth p items) (nth (1+ p) items)))))

すいません、間違った方を投稿しまいました。"\s*"じゃなくて、"\s+"でした。

1
2
3
4
5c5
<     (let ((items (ppcre:split "\\s*" (read-line str nil nil))))
---
>     (let ((items (ppcre:split "\\s+" (read-line str nil nil))))

普通に。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import std.stdio;
import std.string;

void main(){
    string[] amida = ["A B C D E",
                      "| | |-| |",
                      "|-| | |-|",
                      "| |-| |-|",
                      "|-| |-| |",
                      "|-| | | |"];

    auto pos = cast(char[])amida[0].dup;
    foreach(s; amida[1..$]){
        foreach(i, c; s[0..($ - 1)]){
            if(c == '-'){
                auto tmp = pos[i - 1];
                pos[i - 1] = pos[i + 1];
                pos[i + 1] = tmp;
            }
        }
    }
    amida ~= cast(string)pos;
    writefln(amida.join("\n"));
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
def amida(as:String) = {
  val h::t = as.trim.split("\n").toList
  val r = h.toArray
  t.foreach(_.toList.zipWithIndex.filter(_._1=='-').foreach{case (_,i) =>
    var tmp = r(i-1)
    r(i-1) = r(i+1)
    r(i+1) = tmp
  })
  h::t:::List(r.mkString("")) mkString "\n"
}

val a = """
A B C D E
| | |-| |
|-| | |-|
| |-| |-|
|-| |-| |
|-| | | |
"""

println(amida(a))
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
switch(['|'], [A], [A]).
switch(['|','-','|'|S], [A,' ',B|L], [B,' ',C|L1]) :-
    switch(['|'|S], [A|L], [C|L1]).
switch(['|',' ','|'|S], [A,' ',B|L], [A,' ',C|L1]) :-
    switch(['|'|S], [B|L], [C|L1]).

amida_sub(Hs, []) :- atom_chars(H, Hs), writeln(H).
amida_sub(Hs, [X|L]) :- writeln(X), atom_chars(X, Xs),
    switch(Xs, Hs, H1), amida_sub(H1, L).

amida(A) :- concat_atom([H|L], '\n', A), writeln(H),
    atom_chars(H, Hs), amida_sub(Hs, L).

:- amida('A B C D E
| | |-| |
|-| | |-|
| |-| |-|
|-| |-| |
|-| | | |').

タグ書き間違えました,SWI-Prologです.

ものすごく適当に…。 横棒をタプルのリストとして受け取って、名前を入れ替えます。

1
2
3
4
5
6
7
let rec amida names = function
    [] -> names
  | ((h1,h2)::tl) ->
    let t1,t2 = names.(h1),names.(h2) in
      names.(h1) <- t2;
      names.(h2) <- t1;
      amida names tl;;
短かさと, 正規表現の小手先にこだわりました. 
標準入力とかから, アミダクジを与えて下さい. 
1
2
3
$_=<>;print;while($b=$a=<>){print$a;chomp$a;$i=1;$a=~s/(-|\|)/$1eq'-'?' ':'(.)'
/eg;$b=~s/(\|-\||\|)/$1eq'|'?'$'.$i++:'$'.++$i.' $'.($i++ -1)/eg;eval"s/$a/$b/"
}print

おなじ方法のものを, bourne shell + sed で

1
2
3
4
5
6
7
#! /bin/sh
cat $1;t="mktemp tmp.XXXXXX";F=`$t`;R=`$t`;t=`$t`;head -n1 $1>$F;cat $1|sed \
-e1d >$R;while ! test 0 = `cat $R | wc -l`;do X=;r=;i=1;p=0;L=`head -n1 $R|sed\
 -e 's/|-|/-/g'|sed -e 's/|/+/g'`\ `head -n1 $R |sed -e 's/-/ /g'`;for c in $L
do case $c in -)r=$r" \\"`expr 1 + $i`" \\$i";p=2;;+)r=$r" \\"$i;p=1;;\|)
X=$X'\(.\) ';;esac;i=`expr $i + $p`;done;s=s/`echo $X`/`echo $r`/;cat $F|sed \
-e "$s">$t; cp $t $F;cat $R|sed -e '1d' > $t;cp $t $R;done; cat $F;rm $R $F $t;
ファイルの内容を結果付きで返却します。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<?php
function Amida($file_path)
{
    if (!file_exists($file_path)) {
        return NULL;
    }
    $lines = file($file_path);
    $org = array_shift($lines);
    $p = $org;
    foreach ($lines as $line) {
        for ($i = 1; $i < strlen(trim($line)); $i+=2) {
            if ($line[$i] == '-') {
                $sub = array($p[$i+1]=>$p[$i-1], $p[$i-1]=>$p[$i+1]);
                $p = strtr($p, $sub);
            }
        }
    }
    return $org.implode('', $lines).$p;
}
?>