1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AHC027で最低限のローカルテスト環境を作った話

Last updated at Posted at 2023-12-12

この記事はSLP-KBIT AdventCalendar202312日目の記事です。

目次

・はじめに
・ローカルテスト手順
・seeds.txtに乱数を書く
・提出用コードをファイル入出力用にする(C++)
・vis.rsをn回実行する(Rust)
・おわりに

はじめに

こんにちは、kaede9030です。先週行われたAHC027のローカルテスト環境構築について書きます。 最低限 のため並列処理とかはしません。
AHCやローカルテスト環境の必要性についてはこの記事を読んでください。

ローカルテスト手順

まずRust実行環境を用意し、問題文のページからローカル版ツールを入手しましょう。

tools
|---in
|---src
|---target
|---seeds.txt
|---README.html
|---その他.なにか

その中のREADME.htmlを読み、ローカルテストの手順を考えますが、とりあえず動くことを確かめます。

入力生成

toolsディレクトリに移動し、入力生成コマンド

cargo run -r --bin gen seeds.txt

を打つと、inディレクトリに0000.txt~0099.txtができると思います。

ビジュアライザ実行

toolsディレクトリ内にin.txt, out.txtを作り、それぞれ問題文の入力例と出力例を書き、ビジュアライザ実行コマンド

cargo run -r --bin vis in.txt out.txt

を打つと、toolsディレクトリにvis.htmlが生成され、標準出力には

Score = 3302174

と表示されるはずです。


さて、ここまでまとめると

  1. 入力.txtを用意する
  2. 出力.txtを用意する(コンテスト提出用コードをファイル操作に対応させる)
  3. 上2つを使ってビジュアライザを動かす
    ↑これらを行えばよさそうです。

seeds.txtに乱数を書く

README.html によると

in ディレクトリに予め生成された seed=0~99 に対する入力ファイルが置かれています。 より多くの入力が欲しい場合は、seeds.txt に欲しい入力ファイルの数だけ乱数seed値(符号なし64bit整数値)を記入し、以下のコマンドを実行します。

cargo run -r --bin gen seeds.txt

らしいので、toolsディレクトリに乱数をseeds.txtに書き込むプログラム

seeds_gen.c
//言語は何でもいい
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main(void){
    int n=1000; //生成したい数
    FILE *fp;
    fp=fopen("seeds.txt","w");
    if(fp==NULL){
        printf("cannot open file\n");
        exit(1);
    }
    srand((unsigned)time(NULL)); //今更だけどこれ符号なし32bitでは…
    for(int i=0;i<n;i++){
        unsigned tmp=rand();
        fprintf(fp,"%u\n",tmp);
    }
    fclose(fp);
    return 0;
}

を置き、実行します。先のコマンド(cargo run -r --bin gen seeds.txt)も実行。

提出用コードをファイル入出力用にする

自分が普段使う言語で提出用コードを書き、先ほどの入力ファイル(0000.txtなど)を受け取り、答えを出力ファイル(0.txtなど)に書き込みます。このファイルはinディレクトリに置いてください。
こんな感じ↓

submit.cpp
#include<bits/stdc++.h>   //問題文のDFSするだけACコード
using namespace std;

int N;
int di[4]={0,1,0,-1},dj[4]={1,0,-1,0};
string dir="RDLU";
vector<string> h(39);
vector<string> v(40);
vector<vector<int>> d(40,vector<int>(40));
string ans="";

void dfs(int i,int j,vector<vector<bool>>* vis){
    (*vis)[i][j]=true;
    for(int k=0;k<4;k++){
        int ii=i+di[k],jj=j+dj[k];
        if(0<=ii&&ii<N&&0<=jj&&jj<N&&!(*vis)[ii][jj]){
            if((di[k]==0&&v[i][min(j,jj)]=='0')||(dj[k]==0&&h[min(i,ii)][j]=='0')){
                ans+=dir[k];
                dfs(ii,jj,vis);
                ans+=dir[(k+2)%4];
            }
        }
    }
}

int main(void){
    for(int i=0;i<1000;i++){   //1000個分ファイル処理
        string filename="";
        string num=to_string(i);
        int x=4-num.size();
        for(int j=0;j<x;j++){
            filename+="0";
        }
        filename+=num+".txt";
        ifstream infile(filename);
        if(infile.is_open()){
            infile>>N;
            for(int j=0;j<N-1;j++){
                infile>>h[j];
            }
            for(int j=0;j<N;j++){
                infile>>v[j];
            }
            for(int j=0;j<N;j++){
                for(int k=0;k<N;k++){
                    infile>>d[j][k];
                }
            }
            infile.close();
            vector<vector<bool>> visited(N,vector<bool>(N,false));
            dfs(0,0,&visited);
        }else{
            cout<<"cannot open "<<filename<<endl;
            exit(1);
        }
        string fname=to_string(i)+".txt";
        ofstream outfile(fname);
        if(outfile.is_open()){
            outfile<<ans<<endl;
            outfile.close();
            ans="";
        }
    }
    //cin>>N;   //提出用
    //for(int i=0;i<N-1;i++){
    //    cin>>h[i];
    //}
    //for(int i=0;i<N;i++){
    //    cin>>v[i];
    //}
    //for(int i=0;i<N;i++){
    //    for(int j=0;j<N;j++){
    //        cin>>d[i][j];
    //    }
    //}
    //vector<vector<bool>> visited(N,vector<bool>(N,false));
    //dfs(0,0,&visited);
    //cout<<ans<<endl;
    return 0;
}

実行するとinディレクトリに.txtファイルがいっぱいできると思います。
入力ファイル0000.txtと出力ファイル0.txtが対応しています。toolsディレクトリのin.txt, out.txtは使わないので消しても構いません。

vis.rsをn回実行する

ここで躓きました。まず思いついた方法は、提出コードをテストケースの数だけ繰り返したように、vis.rsの中にループの処理を追記するやり方です。

こんな感じ?.rs
fn main(){
    for i in 0..1000{   //テストケースの数だけループ
    //vis.rsにもともとある処理+α
    }
}

この方法は不採用です。理由は、私がRustの知識が皆無だからです。公式のツールをいじるとか怖くてできません。

他の方法は?

vis.rsをいじりたくないならcargo~コマンドをn回実行したらどうか?と思い「Rust コマンド実行」などと検索しているとstd::process::Commandなるものを見つけました。

実装

自力ですべて実装するのは厳しいため、ChatGPT先生を頼ります。文字列難しい

このファイルはtoolsディレクトリに置いてください。

vis_rep.rs
use std::process::Command;

fn main() {
    let mut ave: u64=0;    //u128のほうが安心
    for i in 0..1000{      
        let infile=format!("{:04}",i);    //"0000"~"0999"
        let outfile=i.to_string();        //"0"~"999"
        let inpath=format!("in/{}{1}",infile,".txt");  
        let outpath=format!("in/{}{1}",outfile,".txt");
        let output=Command::new("cargo") //ターミナルに打ち込んでいたコマンド
            .arg("run")
            .arg("-r")
            .arg("--bin")
            .arg("vis")
            .arg(inpath)
            .arg(outpath)
            .output()
            .expect("Failed");
        //ここからほとんどChatGPTが一晩でやってくれました
        if let Ok(stdout) = String::from_utf8(output.stdout) {
            let s=stdout.clone();    //所有権が怖いためコピーしたけどいらない?
            println!("vis.rs output: {}", s);
            if let Some(score_str) = s.split_once('=') {
                if let Ok(score) = score_str.1.trim().parse::<u64>() {
                    println!("Score: {}", score);
                    ave+=score;
                } else {
                    println!("Error: Unable to parse score as integer");
                }
            } else {
                println!("Error: Invalid format for score");
            }
        }
    }    //私が書くとこんなに丁寧にエラー処理しない()
    println!("average score is {}",ave/1000);
}

何をしているかとても簡単に説明すると

cargo run -r --bin vis in/0000.txt in/0.txt

このコマンドを繰り返し実行し、標準出力に

vis.rs output: Score = 123456789

Score: 123456789

と、スコアを表示するか、エラーを表示します。最後に平均スコアも表示されます。

おわりに

汚コード、過不足しかない説明の中、ここまで読んでくださりありがとうございます。
これだけローカルテスト環境について書いておきながら、私は問題文のサンプルACコードしか提出していません。私がRust公式文書を漁っていた時間は何だったのでしょうか..
茶下位こーだーにしては頑張ったということでこの記事はおしまいです。

他の人のAHC環境構築記事などいろいろ

参考資料

https://doc.rust-jp.rs/book-ja/
https://doc.rust-jp.rs/rust-by-example-ja/std_misc/process.html


昨日の記事:https://qiita.com/nao_bc/private/20f802ea9ac41a634cb6
明日の記事:https://qiita.com/saimagu/private/0c939e67c896cd74ac7d

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?