Watch 'em ตกเหมือนโดมิโน


22

คุณอาศัยอยู่ในอาคารที่มีความกว้าง 80 ตัวอักษร คุณรู้สึกเบื่อดังนั้นคุณตัดสินใจที่จะเล่นโดมิโน ไม่ไม่ใช่คนที่น่าเบื่อที่ดูเหมือน Scrabble ความสนุกที่คุณใช้เวลาหนึ่งชั่วโมงเพื่อให้พวกเขาดูพวกเขาล้มลงในไม่ช้า

ในเทอร์มินัลแต้มแต้มมีลักษณะดังนี้:

|   upright domino
\   left-tilted domino
/   right-tilted domino
__  fallen domino

อย่างที่เราทุกคนรู้กันดีว่าถ้าโดมิโนที่เอียงนั้นสัมผัสกับตั้งตรงโดมิโนที่สองก็เอียงเช่นกัน ข้อยกเว้นเพียงอย่างเดียวคือถ้าโดมิโนเอียงสองอันแตะมัน:

|\ --> \\        /| --> //        /|\ --> /|\

ปรับค่าคงตัวความโน้มถ่วงของเทอร์มินัลเพื่อให้การเปลี่ยนแปลงนี้ใช้เวลา 100 มิลลิวินาที

หากโดมิโนที่เอียงได้รับการสนับสนุนโดยโดมิโนอื่นหรือผนังของเทอร์มินัลการเดินทางจะสิ้นสุดลง

ไม่มีโดมิโนเอียงใน

\||||____||||/__                /|\    /\    /|\                __\||||____||||/

(80 ตัวอักษร) จะเคลื่อนไหวเนื่องจากโดมิโนเอียงทั้งสองได้รับการสนับสนุนจากผนังของเทอร์มินัลและโดมิโนอื่นรองรับทั้งหมด

อย่างไรก็ตามหากพื้นที่ในทิศทางการเอียงว่างเปล่าโดมิโนจะล้มลง:

| \\ --> |__\        // | --> /__|

สถานีปลายทาง ค่าความโน้มถ่วงคงที่ คุณได้รับจุด ...

ในที่สุดก็มีลมเล็กน้อยจากทางด้านซ้ายดังนั้นโดมิโนที่เอียงขวาจะตกลงเร็วกว่าตัวที่เอียงไปทางซ้าย:

|/ \| --> |__\|

งาน

เขียนโปรแกรม / ฟังก์ชั่นที่แสดงภาพเคลื่อนไหวของการเล่นโดมิโนในเทอร์มินัล

รหัสของคุณควรทำดังนี้:

  1. อ่านสตริงจากอินพุตแสดงถึงสถานะเริ่มต้นของแต้ม

    สายนี้จะมีไม่เกิน 80 ตัวอักษรและประกอบด้วยโดมิโนที่อธิบายไว้ข้างต้นและช่องว่างเปล่าเพียงอย่างเดียว

  2. พิมพ์สถานะและรอ 100 ms

  3. เปลี่ยนสถานะตามที่อธิบายไว้ข้างต้น

  4. หากสถานะเปลี่ยนไปให้กลับไปที่ 2

กฎเพิ่มเติม

  • ความยาวของอินพุตสตริงไม่มีผลต่อความกว้างของเทอร์มินัล แม้ว่าสตริงจะสั้นกว่า 80 อักขระผนังของเทอร์มินัลจะยังคงแยกห่างกัน 80 อักขระ

  • ในแต่ละครั้งที่มีการดำเนินการขั้นตอนที่ 2 สถานะควรจะถูกพิมพ์ไปยังตำแหน่งเดียวกันแทนที่สถานะก่อนหน้า

  • เนื่องจากบางภาษาไม่สามารถรอได้ 100 มิลลิวินาทีคุณสามารถรอได้ระหว่าง 50 ถึง 1,000 มิลลิวินาที

  • ใช้กฎมาตรฐานของ

ตัวอย่าง

  • สำหรับสถานะเริ่มต้น

     ||\/||
    

    พิมพ์ดังต่อไปนี้ (อีกอันหนึ่ง):

     ||\/||
     |\\//|
     \\\///
    __\\//__
    
  • สำหรับสถานะเริ่มต้น

    /||||\
    

    พิมพ์ต่อไปนี้

    /||||\
    //||\\
    ///\\\
    
  • สำหรับสถานะเริ่มต้น

    /|||\
    

    พิมพ์ต่อไปนี้

    /|||\
    //|\\
    
  • สำหรับสถานะเริ่มต้น

    |/ \|/ \|/ \|/ \|
    

    พิมพ์ต่อไปนี้:

    |__\|__\|__\|__\|
    
  • สำหรับสถานะเริ่มต้น (80 ตัวอักษร)

    \||||____||||/__                /|\    /\    /|\                __\||||____||||/
    

    พิมพ์ต่อไปนี้

    \||||____||||/__                /|\    /\    /|\                __\||||____||||/
    

คำตอบ:


13

เรติน่า , 87 86 85 ไบต์

ขอบคุณ Dennis ที่ช่วยประหยัด 1 ไบต์

^.{0,79}$
$0 
:`^
<ESC>c
(`/ | \\
__
/\|(?!\\)
//a
(?<!/)\|\\
\\
$
aaaaa
a
aaaa
(a+)+b|a
<empty>

<ESC>ควรถูกแทนที่ด้วยอักขระควบคุมจริง (0x1B) <empty>แสดงถึงบรรทัดต่อท้ายที่ว่างเปล่า คุณสามารถเรียกใช้รหัสข้างต้นจากไฟล์เดียวกับ-sธง

รหัสต้องการเทอร์มินัลที่รองรับรหัสหลบหนี ANSI ฉันไม่สามารถระงับการป้อนบรรทัดในเอาท์พุทของ Retina ได้ดังนั้นฉันต้องล้างคอนโซลทั้งหมดด้วย<ESC>cในแต่ละครั้ง ฉันทดสอบโค้ดด้วยการทุบตีโดยใช้ Mono เพื่อรัน Retina

คำอธิบาย

^.{0,79}$
$0 

เราเริ่มต้นด้วยการเพิ่มช่องว่างหากอินพุตมีอักขระน้อยกว่า 80 ตัว นี่คือ/จุดสิ้นสุดที่ไม่จำเป็นต้องแยกจากกัน

:`^
<ESC>c

ตอนนี้เรา<ESC>cต่อท้ายสตริงซึ่งเป็นรหัสหลบหนี ANSI สำหรับการล้างเทอร์มินัล ดังนั้นทุกครั้งที่มีการพิมพ์สตริงมันจะทำที่ด้านบนของเทอร์มินัล :`สั่ง Retina ที่จะพิมพ์ออกผลของการทดแทนนี้คือกำหนดค่าเริ่มต้น

(`/ | \\
__

(`เริ่มวนซ้ำ เนื่องจากไม่มีการจับคู่)การวนซ้ำจะถือว่าดำเนินต่อไปจนถึงขั้นตอนสุดท้ายของโปรแกรม การวนซ้ำแต่ละครั้งจะจำลองหนึ่งขั้นตอนของแต้มที่ตกลงมาจากนั้น "นอน" สักครู่ นี้แทนที่ขั้นตอนแรก/และต่อไปยังพื้นที่ลง\ __วิธีนี้จะจัดการ/ \กรณีและปัญหาได้อย่างถูกต้องโดยอัตโนมัติเนื่องจากการจับคู่ไม่สามารถซ้อนทับกันและค้นหาจากซ้ายไปขวา ดังนั้น/<sp>จะได้รับการจับคู่และกลายเป็น__เช่นนั้น\ไม่สามารถจับคู่, __\และเราได้รับที่ถูกต้อง

/\|(?!\\)
//a

นี่จะกลาย/|เป็น//หากไม่มีสิ่งใด\อยู่ข้างๆ เราผนวกสิ่งaที่ใหม่นี้/ไม่ได้ยุ่งกับขั้นตอนต่อไป (ซึ่งไม่ควร "รู้" เกี่ยวกับการเปลี่ยนแปลงนี้)

(?<!/)\|\\
\\

สถานการณ์ตรงข้าม: เปลี่ยน|\เป็น\\กรณีที่ไม่มีสิ่งใด/อยู่ข้างๆ เราไม่จำเป็นต้องใส่aที่นี่เพราะเราทำกับขั้นตอนการจำลองนี้แล้ว

ตอนนี้ส่วนที่หลับ ...

$
aaaaa

เพิ่มอีก 5 as ต่อท้ายโค้ด

a
aaaa

เปลี่ยนaเป็น 4 aวินาทีดังนั้นเราจะได้ 20 aวินาทีในตอนท้าย

(a+)+b|a
<empty>

ตอนนี้ส่วนที่สนุก ... ที่เรานอนหลับสำหรับบิตด้วยความช่วยเหลือของbacktracking ภัยพิบัติ มีจำนวนวิธีในการแยกการจับคู่(a+)+ระหว่างการทำซ้ำของกลุ่ม เนื่องจากbสาเหตุที่ทำให้การแข่งขันล้มเหลวเครื่องยนต์จะย้อนรอยและลองชุดค่าผสมเหล่านั้นทุกชุดก่อนที่จะตัดสินใจว่า(a+)+bไม่ตรงกัน สำหรับยี่สิบaวินาทีในตอนท้ายซึ่งใช้เวลาประมาณครึ่งวินาที

ในเวลาเดียวกันเราอนุญาตให้ regex จับคู่เดี่ยวaแต่หลังจากทำการย้อนรอยแล้ว เมื่อการจับคู่นั้นเราแทนที่ด้วยสตริงว่างลบทั้งหมดที่aเราแทรกด้วยเหตุผลหนึ่งหรืออื่น ๆ จากสตริง

ที่ปล่อยให้พิมพ์สตริงที่จุดสิ้นสุดของการวนซ้ำวนซ้ำ ที่นี่มีประโยชน์ที่ฉันยังไม่ได้แก้ไขพฤติกรรมการพิมพ์ของลูปใน Retina เลย ปัจจุบันมีเพียงธงเดียวสำหรับแต่ละขั้นตอนซึ่งระบุว่า "พิมพ์" หรือ "ไม่พิมพ์" ค่าเริ่มต้นคือ "ไม่พิมพ์" ยกเว้นขั้นตอนสุดท้ายในโปรแกรมซึ่งเป็นค่าเริ่มต้นที่ "พิมพ์" แต่สเตจนั้นวนซ้ำดังนั้นมันจึงพิมพ์สตริงปัจจุบันในแต่ละการวนซ้ำ โดยปกติแล้วมันน่ารำคาญมากและคุณเกือบจะต้องเพิ่มสเตจเปล่า ๆ ท้ายถ้าคุณต้องการผลลัพธ์สุดท้ายเท่านั้น แต่ที่นี่ช่วยให้ฉันประหยัดได้สี่ไบต์


6

Javascript (ES6), 206 148 129 158 ไบต์

ในที่สุดฉันก็ได้รับมันลงมาถึงจุดต่ำสุด แต่มันจะไม่ล้างคอนโซลหรือเพิ่มพื้นที่เพิ่มเติม; ปัญหาเหล่านี้ได้รับการแก้ไขแล้ว

c=console;d=s=>{c.clear(s[79]||(s+=' ')),c.log(s),t=s[R='replace'](/\/ | \\/g,'__')[R](/\/\|/g,'//a')[R](/\|\\/g,'\\\\')[R](/a/g,'');t!=s&&setTimeout(d,99,t)}

สำรองเวอร์ชัน 153 ไบต์ซึ่งควรทำงานใน Node.JS:

d=s=>{s[79]||(s+=' '),console.log("\033c"+s),t=s[R='replace'](/\/ | \\/g,'__')[R](/\/\|/g,'//a')[R](/\|\\/g,'\\\\')[R](/a/g,'');t!=s&&setTimeout(d,99,t)}

IMHO มันสนุกมากที่ได้เล่นกับ ลองรุ่น HTML ที่นี่:

อาจมีห้องสำหรับเล่นกอล์ฟที่ดีขึ้น ยินดีต้อนรับข้อเสนอแนะ!


+1 สำหรับการสาธิตที่ทำงานได้ซึ่งเสียเวลาเพียง 10 นาทีและ +1 สำหรับฟังก์ชั่นการสุ่ม อย่างไรก็ตามดังที่เดนนิสกล่าวถึงมันล้มเหลวในกรณีทดสอบครั้งแรก ลอง/หรือ/|และคุณจะเห็นกระเบื้องไม่ตกไปตามที่ควร
dberm22

@Dennis ขอขอบคุณสำหรับการชี้ปัญหาเหล่านี้ ฉันเชื่อว่าฉันได้แก้ไขทั้งสองอย่างแล้ว
ETHproductions

โหนดไม่มีความสุขกับลูกศรอ้วน แต่มันใช้ได้ดีอย่างอื่น คุณสามารถแทนที่\033ด้วยตัวอักษร ESC ไบต์ประหยัด 3 ไบต์
เดนนิส

2

Perl 5, 154 146

ต้องใช้ตัวอักษรชั่วคราวเพื่อรักษาสถานะระหว่าง 2 regexes
เพื่อจัดการกับความเสี่ยงที่บางอย่างเช่น / | | | \ จะจบลงด้วย / / / \ \ แทน / / | \ \

$_=substr(pop.' ',0,80);$|++;while($}ne$_){print"$_\r";$}=$_;s@ \\|/ @__@g;s@/\|(?=[^\\])@/F@g;s@([^/])\|\\@$1\\\\@g;tr@F@/@;select($\,$\,$\,0.1)}

ทดสอบ

$ perl dominos.pl '|\ |\/|||\/|'
|\__\//|\\/__

1
คุณสามารถกำจัดหลาย backslashes ถ้าคุณใช้ตัวคั่นอื่น ๆ กว่าเฉือน - เช่นแทนs, \\|/ ,__,g s/ \\|\/ /__/g
hobbs

เคล็ดลับดี ลืมเกี่ยวกับเคล็ดลับที่ และอีกสองสามไบต์ก็ถูกตัดโดยใช้ชุดที่ถูกทำให้ไร้ผล
LukStorms

2

ES6 , 220 218 195 ไบต์

minified

f=d=>{var e,c=console;if(!d[79])d+=' ';c.clear();c.log(d);e=d;d=d[R='replace'](/\/\|\\/g,'a')[R](/\/ | \\/g,'__')[R](/\/\|/g,'//')[R](/\|\\/g,'\\\\')[R]('a','/|\\');if(e!=d)setTimeout(f,100,d);};

อ่านเพิ่มเติม

f=d=> {
    var e,
    c=console;
    if(!d[79])
        d+=' ';
    c.clear();
    c.log(d);
    e=d;
    d = d[R='replace'](/\/\|\\/g, 'a')  //Substitute '/|\' with 'a' so it doesn't get replaced
        [R](/\/ |  \\/g, '__')     //Replace '/ ' and ' \' with '__'
        [R](/\/\|/g, '//')    //Replace '/|' with '//'
        [R](/\|\\/g, '\\\\')  //Replace '|\' with '\\'
        [R]('a', '/|\\');     //Put '/|\' back
    if(e!=d)
        setTimeout(f,100,d);
};

2
ยินดีต้อนรับสู่ Programming Puzzles & Code Golf! 1. ฉันไม่แน่ใจว่าทำไมคุณใช้สัญลักษณ์ ES6 () = > {และ}()สามารถลบออกจากรหัสของคุณได้ 2. ฉันไม่คิดว่ากล่องแจ้งเตือนเป็นรูปแบบผลลัพธ์ที่ยอมรับได้สำหรับภาพเคลื่อนไหว คุณสามารถฝัง JS ของคุณใน HTML หรือทำการเปลี่ยนแปลงที่จำเป็นเพื่อให้มันทำงานได้จากบรรทัดคำสั่ง 3. ไม่ว่าในกรณีใดรหัสของคุณต้องรอประมาณ 100 ms ระหว่างการพิมพ์สถานะเดียวและถัดไป
เดนนิส

2
ยินดีต้อนรับสู่ PPCG! ฉันขอแนะนำให้ตรวจสอบโพสต์นี้และโพสต์นี้เพื่อช่วยปรับปรุงการเล่นกอล์ฟของคุณ
jrich

ขอบคุณสำหรับคำแนะนำและลิงค์ไปยังเคล็ดลับการเล่นกอล์ฟ มันยังคงยาวไปหน่อย แต่ฉันสั้นลงเล็กน้อยและเพิ่มตัวจับเวลา ฉันจะทำตามเคล็ดลับในเชิงลึกมากขึ้นและดูสิ่งที่ฉันสามารถย่อ
user3000806

1
ที่ควรจะทำงานในอาคารผู้โดยสารตอนนี้ แต่ก็ยังจะไม่พิมพ์สถานะการปรับปรุงเหนือเดิม บน Linux คุณสามารถแก้ไขได้โดยการโทรconsole.log("^[c"+d)แทนโดยที่^[ตัวละคร ESC (หนึ่งไบต์)
Dennis

1
ถ้าคุณเปลี่ยนคนแรกที่.replaceไป[R='replace']แล้วแต่ละคนภายหลังจาก[R]นี้จะลดลงไม่น้อย คุณสามารถบันทึกสองสามไบต์โดยใช้setTimeout(f,100,d)แทนการตั้งค่าปัจจุบัน
ETHproductions

2

C #, 335 ไบต์

ไม่ใช่ทางเลือกที่ยอดเยี่ยมของภาษา

ฉันใช้เวลาล่าช้าระหว่าง 50 ถึง 1,000 ในการเลือกหมายเลขสองหลัก

เพิ่มบรรทัดใหม่และการเยื้องเพื่อความชัดเจน:

namespace System.Threading{
    class P{
        static void Main(string[]z){
            var c=@"/|\,/|\,/|,//,|\,\\,/ ,__, \,__".Split(',');
            for(string a=z[0].PadRight(80),b="";a!=b;){
                Console.Clear();
                Console.Write(b=a);
                Thread.Sleep(99);
                a="";
                for(int i,j;(i=a.Length)<80;)
                    a+=(j=Array.FindIndex(c,d=>b.Substring(i).StartsWith(d)))%2==0
                        ?c[j+1]
                        :b.Substring(i,1);
            }
        }
    }
}

1

PHP, 175 ไบต์

$i=sprintf("%-80s",$argv[1]);$p='preg_replace';do{echo($o=$i)."\r";$i=$p('(/\|\\\\(*SKIP)(?!)|(?|(/)\||\|(\\\\)))','$1$1',$p('(/ | \\\\)','__',$i));usleep(1e5);}while($i!=$o);

ยกเลิก minified:

$input = sprintf("%-80s",$argv[1]);
do {
  echo $input."\r";
  $old = $input;
  $input = preg_replace('(/ | \\\\)','__',$input);
  $input = preg_replace('(/\|\\\\(*SKIP)(?!)|(?|(/)\||\|(\\\\)))','$1$1',$input);
  usleep(100000);
}
while( $input != $old);

โดยทั่วไปแล้วสนามกอล์ฟ regex อันดับแรกแบนโดมิโนที่ตกลงมาที่มีพื้นที่ (และเนื่องจากคำสั่งจับคู่จากซ้ายไปขวาทำให้ "ลม" พัด) จากนั้นส่วนที่น่าเกลียดมา (สาปแช่งคุณเฉือน!)

  • จับคู่/|\แล้วข้าม
  • จับคู่(/)|และแทนที่ด้วย//
  • จับคู่|(\)และแทนที่ด้วย\\

สิ่งนี้ทำให้โดมิโนตก ในที่สุดเพียงรอ 100ms สำหรับขั้นตอนต่อไป

ใช้()เป็นตัวคั่นบน regex หมายความว่า/ไม่จำเป็นต้องหลบหนีซึ่งจะช่วยให้น้อยที่สุด!


คุณได้รับอนุญาตให้รอ 50ms แทน 100 ประหยัด 1 char;) PHP อนุญาต 10 ^ 5 หรือไม่
BlueCacti

1

POSIX shell + sed, 144

sed 's/^.\{1,79\}$/& /;s/.*/printf '"'&\\r'"';sleep .1/;h;:;s,/|\\,/:\\,g;s,\(/ \| \\\),__,g;s,/|,//,g;s,|\\,\\\\,g;H;t;x;y/:/|/;s/\\/\\\\/g'|sh

นี่เป็นสองส่วน งานหลักของการโค่นล้มโดมิโนคือการsedแทนที่รูปแบบมาตรฐาน เราชั่วคราวเปิด/|\เข้าไป/:\เพื่อปกป้องมันฟื้นตัวในตอนท้าย

s/^.\{0,79\}$/& /
h

:
s,/|\\,/:\\,g
s,\(/ \| \\\),__,g
s,/|,//,g
s,|\\,\\\\,g
H
t

x
y/:/|/

เนื่องจากsedยังไม่มีวิธีการแทรกความล่าช้าใด ๆ (ฉันดู terminfo / termcap แต่ไม่สามารถหาวิธีมาตรฐานได้) ฉันจึงใส่บรรทัดแต่ละบรรทัดprintf "...\r"; sleep .1 เพื่อพิมพ์บรรทัดทุก ๆ 100ms ที่จริงฉันทำสิ่งนี้เป็นครั้งแรกเมื่อเรามีเพียงหนึ่งบรรทัดเนื่องจากอักขระในคำสั่งจะไม่ถูกแทนที่ด้วยการแทนที่ของการโค่นล้มใด ๆ

ทั้งหมดผ่านการทดสอบโดยใช้dashและ GNU coreutilsพร้อมPOSIXLY_CORRECTกำหนดในสภาพแวดล้อม

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.