Beecrowd - Ajuda para iniciantes na programação em Java
Fala, turma. Se você é um iniciante na programação Java e quer tentar aprender um pouco dela resolvendo questões no Beecrowd (antigo Uri Online Judge), este post é para você.
Problema e Solução
Há algumas semanas, fiquei impaciente em resolver questões do Beecrowd por sempre ter que testar a aplicação dentro do site, e isso demorava um pouco para carregar minha resposta que, em seguida, devolveria o feedback dela (se estava correta ou não).
E como eu desejava aprender toda a sintaxe básica do Java, usar a IDE não me parecia interessante, pois há diversos mecanismos de autocompletar para ajudar e eu queria algo mais sólido, que por decisão própria usei o VIM como ferramenta de edição de texto por linha de comando.
Em alguns casos, me incomodava o fato de haver alguns erros semanticos que eu poderia ter evitado testando-o na minha máquina, antes de colar o código elaborado no Beecrowd. Porém era custoso ter de compilar o programa usando o javac <programa.java>
e em seguida executá-lo com java <programa>
. (Caso esteja curioso e tenha dúvidas, procure entender como funciona o processo de compilação de um programa java). Nisso, notei que ambas as formas de testar era ruim e demorada.
Além disso, outro problema que tive, era poder guardar meus códigos em um repositório, para manter a prática de commitar e documentar meu aprendizado.
Dessa forma, decidi criar um script para facilitar meu processo de compilação e execução, que me permitia, após criar o programa java, compilar, executar e apagar o .class
após o fim da execução do código pelo terminal.
E nisso, elaborei o seguinte script run.sh
:
#!/bin/bash
# run.sh
# Verifica se o argumento foi passado
if [ -z "$1" ]; then
echo "Uso: bash run.sh <diretório ou caminho para Main.java>"
exit 1
fi
# Acessa o argumento passado
ARG=$1
# Verifica se é um diretório ou um arquivo
if [ -d "$ARG" ]; then
DIR=$ARG
FILE="$DIR/Main.java"
elif [ -f "$ARG" ]; then
FILE=$ARG
DIR=$(dirname "$ARG")
else
echo "Erro: Argumento inválido. Passe um diretório ou o caminho para Main.java."
exit 1
fi
# Compila o arquivo Main.java
javac "$FILE"
# Verifica se a compilação foi bem-sucedida
if [ $? -eq 0 ]; then
# Executa a classe Main
java -cp "$DIR" Main
# Remove o arquivo .class após a execução
rm "$DIR/Main.class"
else
echo "Erro na compilação do arquivo."
exit 1
fi
Aqui está um exemplo de execução do código:
$ bash run.sh Begginer/1000/
Hello World!
Outra maneira de facilitar mais ainda, é você criar um alias para o run
no seu ~/.bashrc
. No meu caso, coloquei o seguinte nele:
# alias for run.sh Java
alias run='bash ~/Github/Beecrowd/run.sh'
Note que o caminho de execução está ~/Github/Beecrowd/run.sh
, e no seu caso, caso queria replicar, você deve colocar o caminho do seu script run.sh
da sua árvore de arquivos.
E em seguida, executei source ~/.bashrc
para carregar o bash com as mudanças adicionadas. Me permitindo rodar o código da seguinte maneira (dentro do meu diretório correspondente):
$ run Begginer/1000/
Hello World!
Todo o código está no meu repositório com um README
bem explicativo de como está toda estrutura do repositório. E caso você tenha interesse em copiar: Sinta-se à vontade!
É isto, espero ter ajudado em algo. Qualquer dúvida, pode comentar que terei prazer em responder!
Qual versão do Java vc está usando?
Pois a partir do Java 11, para um único arquivo, é possível rodar direto, sem fazer o passo de compilação à parte. Ou seja, dá pra chamar assim:
java Beginner/1000/Main.java
Desta forma, ele já compila e roda, tudo de uma vez. Com o detalhe que o arquivo .class
nem sequer é gerado, ou seja, vc não precisa se preocupar em apagá-lo depois.
Esta funcionalidade está definida na JEP 330 (Launch Single-File Source-Code Programs), e como o próprio nome diz, serve para "programas de um arquivo só" (que parece ser justamente o seu caso).
Sendo assim, daria para mudar o run.sh
para:
#!/bin/bash
# Verifica se o argumento foi passado
if [ -z "$1" ]; then
echo "Uso: bash run.sh <diretório ou caminho para Main.java>"
exit 1
fi
# Verifica se é um diretório ou um arquivo
if [ -d "$1" ]; then
FILE="$1/Main.java"
elif [ -f "$1" ]; then
FILE=$1
else
echo "Erro: Argumento inválido. Passe um diretório ou o caminho para Main.java."
exit 1
fi
# Compila e executa o arquivo (ou mostra mensagem, caso ocorra algum erro)
java "$FILE" || echo "Erro na compilação/execução do arquivo."
E em vez de bash run.sh
, uma sugestão é adicionar a permissão de execução ao arquivo:
chmod u+x run.sh
Desta forma, o usuário terá permissão de execução, aí basta rodá-lo como ./run.sh [argumentos]
.
Claro que vc pode dar permissões para todos com
a+x
, entre outras variações. Mas somente para o usuário rodar,u+x
é o suficiente.
PS: para projetos com mais arquivos, dependências, etc, aí não recomendo fazer tudo na mão. Neste caso, ferramentas como o Maven ou Gradle são mais adequadas (mais aí acho que já estou desviando demais do assunto).
Também usei muito o Beecrowd e tinha um script assim. Porém o meu validava a entrada e saída também. Vou deixar aqui para referência.
Minha estrutura:
Eu resolvia os exercícios em c++ e tinha essa estrutura de pastas:
workspace
- resolvidos
- categoria (iniciante, geometria ...)
- 1001.cpp
- 1002.cpp
- in (arquivo com a entrada do beecrowd)
- out (arquivo produzido pelo meu programa)
- expected (arquivo com a saída do beecrowd)
- 1081.cpp (arquivo que estou trabalhando no momento)
- run.sh
run.sh:
g++ $1.cpp -o $1
/usr/bin/time -v ./$1 < in > out
code --diff out expected
O que isso faz?
usarei como exemplo ./run.sh 1081
g++ $1.cpp -o $1
-> compila o arquivo 1081.cpp
produzindo o programa 1081
(sim, sem extensão)
/usr/bin/time -v ./$1 < in > out
-> chama o programa time, que calcula quanto tempo demora para um programa executar e chama o programa 1081
passando para ele a entrada e escrevendo o que ele printar na saída
code --diff out expected
-> compara o arquivo produzido com o esperado
FYC: trabalhando com entrada e saída de arquivos direto no terminal
./$1 < in > out
se digitar somente isso quando o terminal vê o operador '<' ele passa todo o conteúdo do arquivo para dentro do programa. como se você tivesse digitado aquilo.
se o terminal encontrar o operador '>' vai escrever a saída do programa no arquivo substituindo completamente o conteúdo dele. útil nesse caso quando roda várias vezes
se o terminal encontrar o operador '>>' vai escrever a saída do programa no final do arquivo sem apagar o conteúdo existente. Útil para logs