Arquivo da tag: programação

Como copiar textos da Folha e outros sites que não deixam

Alguns sites começaram a abusar de um recurso super interessante do JavaScript para acabar com uma das características mais importantes da Internet: a capacidade de copiar/colar.

O tratamento dos clipboard events (oncut, oncopy e onpaste) deveria servir para permitir que os programadores façam coisas legais quando você copia/cola um texto (por exemplo, um processador de textos online pode inserir/remover formatação), mas tenho visto cada vez mais ele ser usado para adicionar uma mensagem de copyright no final de um texto copiado, impedir usuários leigos de copiarem textos na web e evitar que se cole coisas que você copiou em formulários.

O que mais me incomoda (e que me levou a escrever esta postagem) é que, hoje, quem copia um trecho de uma reportagem da Folha (para guardar, compartilhar numa rede social ou o que quer que seja) acaba colando:

Para compartilhar esse conteúdo, por favor utilize o link http://www1.folha.uol.com.br/fsp/bla-bla-bla ou as ferramentas oferecidas na página. Textos, fotos, artes e vídeos da Folha estão protegidos pela legislação brasileira sobre direito autoral. Não reproduza o conteúdo do jornal em qualquer meio de comunicação, eletrônico ou impresso, sem autorização da Folhapress (pesquisa@folhapress.com.br). As regras têm como objetivo proteger o investimento que a Folha faz na qualidade de seu jornalismo. Se precisa copiar trecho de texto da Folha para uso privado, por favor logue-se como assinante ou cadastrado.

Não é incrível (e sintomático) que o grupo que gerencia o portal mais importante da Internet no Brasil (UOL) tenha uma concepção tão atrasada da rede? Ok, não dá nem pra dizer que isso nos surpreende depois da censura da Falha e do paywall.

Sem mais delongas: isso merece ser hackeado. Neste post, proponho algumas soluções simples para você poder voltar a copiar e colar no seu navegador como sempre fez. Minha preferida, como sempre, é a última.

Solução trivial para quem usa Linux

Antes de sugerir soluções de verdade, convém observar que quem usa Linux (X11) pode copiar selecionando um texto (sem apertar Ctrl+C ou qualquer outra combinação esdrúxula de teclas) e colar apertando o botão do meio do mouse. Quando se copia/cola dessa forma, o navegador não emite os temidos eventos oncopy/onpaste (ou seja, tudo funciona normalmente).

Rodolfo Mohr também observou que você pode copiar um texto selecionando-o, clicando com a tecla direita na seleção e em “Pesquisar no Google”. Uma aba vai abrir com a pesquisa no Google e você pode copiar o texto lá. É um hack válido, embora incômodo.

Somente Firefox: usando about:config

Se você usa Firefox, pode desabilitar os clipboard events digitando, na barra de endereços, em about:config. Talvez ele diga que é perigoso e peça para você clicar num botão dizendo que sabe o que está fazendo. Pode confiar. Em seguida, procure a chave dom.event.clipboardevents.enabled e clique duas vezes nela para mudar seu valor para false. Reiniciando o navegador, o recurso copiar/colar estará funcionando normalmente (ou talvez nem precise reiniciá-lo).

Extensões (para Firefox, Chrome e Opera)

Não tem o que explicar. Simplesmente clique no nome do seu navegador e instale: Firefox, Chrome, Opera.

Editado em 01/04/2014, 22:30: A extensão que eu havia colocado para Chrome só desabilita o tratamento de eventos onpaste em formulários. Se você conhecer alguma extensão similar a do Firefox ou a do Opera, me avise pelos comentários.

Desabilitando sob demanda via JavaScript

É muito importante ter em mente que aplicações web como processadores de texto podem usar os eventos oncut/oncopy/onpaste para coisas úteis. Por isso, é desejável desabilitar esses eventos somente em sites específicos.

Não encontrei nenhuma extensão que faça isso, mas um código simples em JavaScript para recuperar o comportamento padrão dos eventos em um determinado site (testei no Firefox e no Chrome) é:

all = document.querySelectorAll("*");
fn = function(e) {
    e.stopPropagation();
    return true;
}
for (var i = 0; i < all.length; i++) {
    all[i].oncut = fn;
    all[i].oncopy = fn;
    all[i].onpaste = fn;
}

Se digitarmos isso no console (Shift+Ctrl+J), as funções copiar/colar devem voltar a funcionar.

Userscript

A solução anterior nos permite criar um userscript para desabilitar o tratamento dos eventos apenas no site da Folha:

// ==UserScript==
// @name Permite copiar textos da Folha
// @include http://*.folha.uol.com.br/*
// ==/UserScript==
 
window.onload = function() {
    all = document.querySelectorAll("*");
    fn = function(e) {
        e.stopPropagation();
        return true;
    }
    for (var i = 0; i < all.length; i++) {
        all[i].oncut = fn;
        all[i].oncopy = fn;
        all[i].onpaste = fn;
    }
}

Portanto, se você quiser copiar do site da Folha sem preocupações (e sem desabilitar os eventos em outros sites), pode instalar as extensões GreaseMonkey (Firefox) ou TamperMonkey (Chrome), e então esse userscript clicando neste link: falha.user.js.

Bookmarlet

Acho o método acima (do userscript) o melhor para copiar da Folha. No entanto, é conveniente ter um método mais genérico. Por isso, criei um bookmarklet, isso é, um pequeno script que podemos executar clicando num botão na barra de favoritos (neste caso, para restaurar o comportamento padrão das funções copiar/colar).

Aqui está ele: Restaurar copiar/colar

Para instalar, arraste esse link para sua barra de favoritos. Para usar, clique sempre que precisar copiar um texto e então copie normalmente.

Viva a Internet!

Como ler documentos do Scribd

Depois de ouvir esse improviso do André Mehmari sobre Odeon e Choro pro Zé, fiquei com vontade de encontrar a partitura desse belo choro do Guinga. Porém, descobri que infelizmente é extremamente difícil encontrar o songbook “A música de Guinga”.

Procurando na rede, encontrei um torrent com um PDF com qualidade ruim e um documento do Scribd com qualidade boa. O problema é que o Scribd tem um paywall para não deixar as pessoas baixarem ou lerem os documentos que seus usuários colocam lá:

free-preview

Percebi que ele passa todas as imagens corretamente para o navegador e só no lado do cliente muda a opacidade das páginas para elas ficarem semitransparentes. Então escrevi um userscript bem simples (usando jQuery por comodidade) para o Greasemonkey (uma dessas extensões indispensáveis do Firefox) para recuperar a opacidade das páginas do texto e, se necessário, remover essa mensagem “You’re reading a free preview”.

// ==UserScript==
// @name Suppress Scribd Paywall
// @include http://*.scribd.com/doc/*
// @require http://code.jquery.com/jquery-2.0.3.min.js
// ==/UserScript==
 
(function($) {
    $(document).ready(function() {
        window.setInterval(function(){$(".absimg").css("opacity", "1")}, 1000);
        $(".autogen_class_views_read_show_page_blur_promo").on("click", function(e) { $(this).hide(); });
    });
})(jQuery);

Para usar, é só instalar o Greasemonkey no Firefox e depois baixar o userscript scribd.user.js. Resultado:

choro-pro-ze

Como ler notícias ilimitadas de Folha, Estadão e Globo sem cadastro

TL;DR: Instale o Adblock Plus em seu navegador, entre nas suas opções, peça para adicionar seu próprio filtro e adicione o filtro: *paywall*. Você agora deve ser capaz de ler notícias de Folha, Estadão e Globo sem cadastro. Caso tenha interesse em saber o caminho que levou a solução até aqui, continue lendo o post.




A mídia tradicional mudou a forma como publica na internet. A regra agora é que sem cadastro você só pode acessar um determinado número (pequeno) de notícias. O nome do sistema é paywall. Ao chegar no limite, você recebe mensagens como as seguintes:

folha estadao globo
Folha, Estadão e Globo quando você lê muitas reportagens

No caso da Folha, só o cadastro pago dá acesso ilimitado. Nos outros, aparentemente um cadastro gratuito é suficiente. De qualquer forma, por que dar seus dados para esses sites saberem quem você é, como navega e o que gosta de ler? Para quem esses sites vão dar essas informações?

Para além da preocupação com privacidade e anonimato, esse sistema funciona como um bloqueio para que as pessoas não possam ler e disseminar as notícias da internet. Torna a circulação de informações mais difícil e o espaço internético mais privado e menos democrático. Por isso, compartilho aqui um pouco sobre o funcionamento do paywall e algumas formas de contorná-lo.


Os sites não querem que buscadores tenham dificuldade de acessar e indexar seu conteúdo. Tampouco querem bloquear endereços de IP, já que a quantidade de pessoas que usa internet via NAT (compartilhando o mesmo endereço de IP com outras pessoas numa mesma rede) é enorme. Por isso, eles fazem todo o controle não no computador deles (servidor), mas no seu computador (cliente).

Para fazer isso, eles contam com a ajuda do seu navegador. Eles mandam a página sempre da mesma forma e o seu navegador é que faz o trabalho sujo. Roda um programa escrito em JavaScript para olhar pros dados que ele mesmo já tinha registrado anteriormente (os chamados cookies). Baseado nesses dados, redireciona você para outra página (no caso de Folha e Estadão, simplesmente coloca um fundo preto semi-transparente em cima do conteúdo do site).

Isso torna não só possível, como trivial contornar o bloqueio. Basta dizer para o seu navegador não registrar cookies, desativar a execução de JavaScript ou rodar outro programa para anular a ação do programa da grande mídia. Abaixo vou mostrar diversas formas de fazer isso usando o Mozilla Firefox, mas em outros navegadores há formas semelhantes de fazer o mesmo. Como sempre, a última forma é a que eu considero melhor.

Usar janela de navegação privada

A forma mais simples de acessar um conteúdo bloqueado é acessar a página numa janela de navegação privada. Para abrir tal janela, basta usar o atalho Ctrl+Shift+P no Firefox (ou Ctrl+Shift+N no Chromium). Como essa janela não vai usar os cookies que seu navegador tem registrado na janela principal, você vai conseguir acessar o conteúdo proibido normalmente (como se nunca tivesse acessado nenhuma notícia antes). Há pessoas que usam só o modo de navegação privada o tempo todo (uma opção razoável para evitar rastreamento).

Remover cookies individuais

No Firefox, você pode usar Editar → Preferências → Privacidade → Remover cookies individuais para remover cookies registrados no seu computador. Se você remover todos, vai sair automaticamente de todos os sites onde está logado. Como seu objetivo é contornar o paywall, você pode remover cookies somente dos sites que deseja acessar (no caso, procurar globo, folha e estadao na barra de busca da remoção de cookies).

Desativar JavaScript

É possível desativar a execução de programas enviados pelos sites que você acessa no Firefox desmarcando a caixa Permitir JavaScript no menu Editar → Preferências → Conteúdo do Firefox. Dessa forma, você vai perder muitas funcionalidades dos sites, mas navegar mais rápido e não ter que encarar paywall algum.

A extensão NoScript do Firefox torna mais fácil ativar/desativar scripts de determinados domínios.

Desativar CSS

Se você não se importar com leiaute e diagramação da página, Exibir → Estilos da página → Nenhum estilo vai fazer tudo ficar feio, mas o texto legível.

Usar extensão Web Developer

Instalar a extensão Web Developer no Firefox torna ainda mais simples remover cookies de um determinado domínio e desativar JavaScript ou CSS (aparece uma barra embaixo da barra de endereço com botões pra executar essas ações).

Remover lightbox

No caso de Folha e Estadão (que sobrepõe um fundo preto semi-transparente e uma lightbox na página ao invés de redirecionar você para outra página como faz o Globo), é possível fazer a lightbox desaparecer (sem mexer nos cookies ou no JavaScript) usando o modo de inspeção (Ctrl+Shift+I), selecionando os elementos que quer remover e adicionando o CSS display:none; neles. Por meio de um userscript do Greasemonkey seria possível automatizar isso.

Forma definitiva (minha preferida): filtros no Adblock Plus

Adblock Plus é uma extensão do Firefox extremamente eficiente para bloquear publicidades e scripts não desejados. Os seguintes filtros bloqueiam os scripts de paywall de Folha, Estadão e O Globo:

||paywall.estadao.com.br^
||estadao.com.br/paywall/*
||www1.folha.uol.com.br/folha/furniture/paywall/*
||static.folha.com.br/paywall/*
||oglobo.globo.com/servicos/inc/payWall.Conteudo.js
||oglobo.globo.com/plataforma/js/*/minificados/paywall/registraConteudosLidos.js

(Depois de escrever, fiquei pensando que talvez seja razoável bloquear simplesmente *paywall* de uma vez.)

Para usar, basta ter instalado o Adblock Plus, copiar essas regras (todas juntas) e colá-las em Ferramentas → Adblock Plus → Preferências de filtros → Filtros personalizados.

WordPress plugin: Admin Anti-forget Alarm

ATENÇÃO: Este conteúdo foi publicado há 5 anos. Eu talvez nem concorde mais com ele. Se é um post sobre tecnologia, talvez não faça mais sentido. Mantenho neste blog o que escrevo desde os 14 anos por motivos históricos. Leia levando isso em conta.

I wrote a small WordPress plugin to prevent users from publishing a post without excerpt or thumbnail, or with a too big excerpt, or with a too small thumbnail, or with an uppercase-only title.

This screenshot is showing some of the messages the plugin displays in portuguese.

For some of my websites it’s important to require the editors to fix some stuff before publishing something, and it looks like this is a useful feature for other people as well. There is even a Require Thumbnail plugin in the WordPress Plugin Directory that seems to do one of the things I’ve just implemented.

The plugin works with two different types of requirements: ths first generates errors (i.e., you can’t publish if you don’t fix it) and the second generates warnings (i.e., you will receive a message but you can proceed to publish if you really want to do that).

I thought of not releasing the plugin (because it’s written in Portuguese and you don’t have a cool interface to decide what’s required yet), but in a fashion of overstated bazaar I decided to push the code anyway (without putting in the WordPress plugin directory, of course) so that other people can collaborate if they want to. Take a look :)

Github project home page: https://github.com/tmadeira/antiforget
Git repository to clone: https://github.com/tmadeira/antiforget.git
Code (PHP): https://github.com/tmadeira/antiforget/blob/master/antiforget.php
Download: antiforget.zip (this is pre-alpha: I provide no warranty!)

Como mostrar o último post de cada categoria no WordPress?

ATENÇÃO: Este conteúdo foi publicado há 5 anos. Eu talvez nem concorde mais com ele. Se é um post sobre tecnologia, talvez não faça mais sentido. Mantenho neste blog o que escrevo desde os 14 anos por motivos históricos. Leia levando isso em conta.

O WordPress é um dos meus programas preferidos e um dos que mais consome as madrugadas já faz alguns anos. Escrevi inúmeros temas, alguns plugins (um único genérico o suficiente para ser público) e já modifiquei algumas partes do código (embora hoje seja raro isso ser necessário).

Acho muito simples e me divirto ao programar em PHP para a web (talvez porque eu faça isso há uns dez anos). Gosto muito da forma como o WordPress é escrito e, com efeito, seu lema é Code is poetryCódigo é poesia. Sua documentação e seu código são muito didáticos e foram, assim como seus temas e as tabelas no banco de dados, evoluindo de acordo com o tempo: as atualizações sempre têm novas features e formas mais genéricas e mais elegantes de fazer as coisas.

O WordPress é um software livre usado por mais de 15% da web (um número incrível!) e tem uma comunidade que produz várias extensões (temas e plugins). Gosto tanto dele que certa vez (no final de 2007) escrevi um plugin só para ganhar uma camiseta (que é tamanho G e ainda assim uso de vez em quando).

Propaganda e blogagem a parte, me deparei com um problema interessante em um dos sites que administro com ele no último fim de semana (a gente sempre se depara com problemas interessantes no WordPress também): dado uma categoria com várias subcategorias, gostaria de mostrar um link para o último post de cada uma de suas subcategorias na página inicial.

Uma solução trivial seria fazer uma query pedindo os filhos de uma dada categoria (usando a tabela wp_term_taxonomy) e uma query por categoria para descobrir seu último post (usando as tabelas wp_posts e wp_term_relationships). Suponha (até o final desse post) que a categoria-mãe de todas as categorias que eu quero mostrar na página seja a de ID 33. Então, essa solução seria algo como:

<?php
$query = mysql_query("SELECT term_id FROM wp_term_taxonomy
                      WHERE parent = 33 AND taxonomy = 'category'");
while (list($cid) = mysql_fetch_row($query)) {
    $posts_query = mysql_query("SELECT p.ID, p.post_name, p.post_title
                                FROM wp_posts AS p,
                                     wp_term_relationships AS r
                                WHERE p.ID = r.object_ID AND r.term_taxonomy_id = '$cid'
                                ORDER BY p.post_date DESC
                                LIMIT 1");
    if (mysql_num_rows($posts_query)) {
        list($id, $permalink, $title) = mysql_fetch_row($posts_query);
        // Faça o que quiser com o post aqui
    } else {
        echo "A categoria $cid não tem posts.\n";
    }
}
?>

Não deve ser difícil de entender, mas dá pra resolver de forma ainda mais simples que essa. O WordPress é fantástico e usar as funções dele próprio é bem mais simples, genérico e resolve o problema. A função get_categories aceita um monte de parâmetros, mas só precisamos do child_of:

<?php
foreach (get_categories('child_of=33') as $cat) {
    list($post) = get_posts("numberposts=1&category={$cat->term_id}");
    // Faça o que quiser com o post aqui
}
?>

(quem entrou no post procurando a solução pro problema pode parar por aqui se não for nerd)

Porém, eu queria resolver o problema com uma única query. Achei que seria mais elegante resolver o problema todo no banco de dados sem escrever em PHP e achei que poderia ficar mais rápido. Acho que não ficou mais elegante e não faço ideia se fica mais rápido (fiquei com a impressão de que seja pior porque faço JOIN de quatro tabelas enormes), nem acho que tenha volume de dados (ainda) no site em que implementei isso pra realmente me preocupar, mas me diverti fazendo. Então segue o resultado:

SELECT p.ID AS id,
       CASE WHEN (p.post_date > DATE_SUB(CURDATE(), INTERVAL 1 MONTH)) THEN
           p.post_title
       ELSE
           ''
       END AS title,
       GROUP_CONCAT(c.slug) AS cat
FROM wp_posts AS p
INNER JOIN
    (
        SELECT MAX(p.post_date) AS post_date, c.term_ID AS cid, COUNT(p.ID) AS count FROM
            wp_posts AS p,
            wp_term_relationships AS r,
            wp_terms AS c
        WHERE
            p.ID = r.object_ID AND
            c.term_ID = r.term_taxonomy_ID AND
            p.post_status = 'publish' AND
            p.post_type = 'post'
        GROUP BY c.term_id
    ) AS last ON p.post_date = last.post_date
INNER JOIN wp_term_relationships AS r ON p.ID = r.object_ID
INNER JOIN wp_terms AS c ON c.term_id = r.term_taxonomy_ID
INNER JOIN wp_term_taxonomy AS t ON t.term_id = r.term_taxonomy_ID
WHERE
    c.term_id = last.cid
    AND t.parent = 33
    AND t.taxonomy = 'category'
    AND p.post_status = 'publish'
    AND p.post_type = 'post'
    # AND last.count >= 3
GROUP BY p.ID ORDER BY p.post_date DESC, last.count DESC;

A query (que na verdade é duas) ordena o resultado por data, retorna o título vazio caso o post seja de mais de um mês atrás, junta as categorias (separando-as por vírgula) se um mesmo post for o último de mais de uma categoria e neste caso ordena as categorias por ordem decrescente de número de posts na mesma.

(A parte comentada seria para caso eu só quisesse mostrar o último post de categorias com três ou mais posts.)

Usei o resultado da query da seguinte forma:

$q = mysql_query($query); // $query é a string com aquele SQLzão
$print_final_li = false;
$first = true;
while ($a = mysql_fetch_array($q, MYSQL_ASSOC)) {
    echo "\t\t<li>";
    $permalink = get_permalink($a["id"]);
    $title = $a["title"];
    if ($title == "") {
        $print_final_li = true;
        echo "Veja também: ";
    } else if ($first == true) {
        echo "<a href="$permalink" title="$title">";
        echo get_the_post_thumbnail($a["id"], "home-thumbnail",
                                    Array("title" => get_the_title()));
        echo "</a> ";
        $first = false;
    }
    $cats = explode(",", $a["cat"]);
    foreach ($cats as $low) {
        $up = strtoupper($low);
        echo "<a class="cat" href="http://$low.juntos.org.br/"
                 title="Juntos! $up">$up</a> ";
    }
    if ($print_final_li) break;
    echo "<a class="post" href="$permalink" title="$title">$title</a>";
    echo "</li>\n";
}
while ($a = mysql_fetch_array($q, MYSQL_ASSOC)) {
    $cats = explode(",", $a["cat"]);
    foreach ($cats as $low) {
        $up = strtoupper($low);
        echo "<a class="cat" href="http://$low.juntos.org.br/"
                 title="Juntos! $up">$up</a> ";
    }
}
if ($print_final_li) {
    echo "</li>\n";
}

(e se você quiser vê-lo em prática, entre em juntos.org.br e procure por “Juntos pelo Brasil”)

Não ficou bonitinho? Se por um lado gostei da solução, por outro fiquei imaginando que deva ser um SQL tremendamente ingênuo e digno da minha inexperiência com grandes bancos de dados. O que você acha? Consegue pensar numa forma mais simples, mais eficiente e mais elegante de resolver o mesmo problema? Ou ao menos sem subqueries?

Acho que as relações necessárias já ficaram explícitas na query que eu escrevi, mas segue o diagrama do banco de dados do WordPress pra quem precisar:

Retrospective: new plugin for WordPress

ATENÇÃO: Este conteúdo foi publicado há 5 anos. Eu talvez nem concorde mais com ele. Se é um post sobre tecnologia, talvez não faça mais sentido. Mantenho neste blog o que escrevo desde os 14 anos por motivos históricos. Leia levando isso em conta.

I’ve just wrote my first WordPress public plugin, that I’m licensing under GPL v3. This post is to make it public. I’m writing in English because the WordPress plugin directory asks me to provide a plugin page to host the files there and I’ll provide the URL of this post. Update: The plugin is now in the WordPress plugin directory: wordpress.org/extend/plugins/retrospective/


The website of the brazilian newspaper O Estado de São Paulo has a nice way to display news in a retrospective-style (check this screenshot or this link — Flash required).

Wouldn’t it be nice if we could display WordPress posts in our pages and categories in the same way just by using a shortcode? The possibilities are many. That’s why I wrote the Retrospective plugin for WordPress.

It has at least two advantages over the version you just saw:

  1. Does not require Flash (its implementation uses only CSS and JavaScript/jQuery)
  2. Has a option to respect the (time-)scale of the posts.

Its use is very simple. Wherever you add the shortcode [retrospective] the plugin will draw that cool retrospective. The shortcode supports several attributes:

  • count — limit the number of posts to be displayed (default = 10; use -1 to display all)
  • cat — display posts with category IDs comma-separated (default = display all posts)
  • width — the width of the timeline in pixels (default = 600)
  • delay — the time of the focus change animation in milisseconds (default = 1000)
  • scale — if set, respect the time scale in the distances between the points in the timeline (default = false)
  • image_width, image_height — the dimensions of the thumbnail images in pixels (default = 300×180)
  • image_border_size — the size of the image’s border in pixels (default = 7)
  • image_border_color — the color of the image’s border in hexa RGB (default = 000000)
  • image_margin — the space between the images (default = 5)
  • date_format — the date format in PHP format (default = d/m/Y)

For example, here is a [retrospective count=5 cat=20 image_border_color=2F5971] call:


Some screenshots

Here is a screenshot from juntos.org.br with scale=true (in the link you can see it working):

And here is a screenshot from a fresh WordPress install (TwentyEleven theme without modifications):


Customizing

Post thumbnails

For better results, I suggest always adding post thumbnails to your posts and using registered image sizes in image_width and image_height attributes.

Styling retrospectives

The generated HTML is very easy to style (but just be careful with margins and paddings, they’re set with !important attribute — I did it to try not to break with any theme). Here is a sample:

<div id="retro-uniquehash" class="retrospective">
    <!-- TIMELINE -->
    <ul class="time">
        <li rel="0"><a href="permalink" style="left:0px;"><span>date</span></a></li>
        <li rel="1"><a href="permalink" style="left:300px;"><span>date</span></a></li>
        <li rel="2"><a href="permalink" style="left:600px;"><span>date</span></a></li>
    </ul>
 
    <!-- PHOTOS -->
    <div class="photos">
        <ul>
            <li rel="0"><a href="permalink" title="title">
                <img src="file" class="wp-post-image" /></a></li>
            <li rel="1"><a href="permalink" title="title">
                <img src="file" class="wp-post-image" /></a></li>
            <li rel="2"><a href="permalink" title="title">
                <img src="file" class="wp-post-image" /></a></li>
        </ul>
    </div>
 
    <!-- POSTS -->
    <ul class="posts">
        <li rel="0"><a href="permalink" title="title"><h2>Title <span>(date)</span></h2>
            <p>Excerpt</p></a></li>
        <li rel="1"><a href="permalink" title="title"><h2>Title <span>(date)</span></h2>
            <p>Excerpt</p></a></li>
        <li rel="2"><a href="permalink" title="title"><h2>Title <span>(date)</span></h2>
            <p>Excerpt</p></a></li>
    </ul>
</div>

Styling a specific retrospective

The generated hash takes in consideration all the attributes sent to the shortcode and also how many retrospectives appeared before in the parsing of the actual page. I made it that way to allow users to set up two exactly equal retrospectives in the same page. Because of that, I don’t recommend setting styles for #retro-uniquehash. I think a reasonable solution for this issue is to make something like

<div id="something_that_makes_sense">[retrospective]</div>

when inserting the shortcode and then styling #something_that_makes_sense .retrospective.


Download

Here is the code for download:* retrospective.zip
* Warning: Please consider I’m using a bazaar approach here. Be aware that the plugin probably has a lot of bugs (and please tell me if you catch any).

I hope you enjoy it. Have fun and please let me have your feedback! :)

Triângulo de Pascal mod m

ATENÇÃO: Este conteúdo foi publicado há 6 anos. Eu talvez nem concorde mais com ele. Se é um post sobre tecnologia, talvez não faça mais sentido. Mantenho neste blog o que escrevo desde os 14 anos por motivos históricos. Leia levando isso em conta.

Este post possui intencionalmente apenas imagens e códigos.

Uso do programa que gera triângulo de Pascal mod m

mod 2

Pascal's Triangle mod 2

mod 3

Pascal's Triangle mod 3

mod 5

Pascal's Triangle mod 5

mod 7

Pascal's Triangle mod 7

mod 12

Pascal's Triangle mod 12

mod 23

Pascal's Triangle mod 23

Código-fonte (com alguns bugs inofensivos — procure XXX)

/* pascal -- generate colored Pascal's triangles in XPM format
 
   Copyright (C) 2011 Tiago Madeira <madeira@ime.usp.br>
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3, or (at your option)
   any later version.
 
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
 
   You can read a copy of the GNU General Public License at
   http://www.gnu.org/licenses/gpl-3.0.txt */
 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
#include <math.h>
 
#define DEFMOD 2
#define DEFSIZE 300
#define DEFPADDING 8
 
int mod = DEFMOD;
int size = DEFSIZE;
int padding = DEFPADDING;
 
char makecolors[6][4] = {
	"001", "010", "100", "110", "101", "001"
};
 
struct option longopts[] = {
	{"help", 0, 0, 'h'},
	{"padding", 1, 0, 'p'},
	{"size", 1, 0, 's'},
	{"mod", 1, 0, 'm'},
	{0, 0, 0, 0}
};
 
char *program_name;
 
void try_help() {
	fprintf(stderr, "Try `%s --help' for more information.\n", program_name);
}
 
void help() {
	printf("Usage: %s [OPTION]... [FILE]\n", program_name);
	printf("Generate colored Pascal's triangles (XPM format).\n\n");
	printf("Mandatory arguments to long options are mandatory for short options too.\n\n");
	printf("  -h, --help        print this help\n");
	printf("  -m, --mod=M       paint with different colors numbers mod M\n");
	printf("  -p, --padding=SZ  image padding (margin) in pixels\n\n");
	printf("  -s, --size=SZ     generate SZ lines of Pascal's triangle\n\n");
	printf("With no FILE, or when FILE is -, write to standard output.\n\n");
	printf("Report bugs to <madeira@ime.usp.br>.\n");
}
 
int baselog(int n, int base) {
	return ceil(log(n) / log(base));
}
 
void *xmalloc(size_t size) {
	void *x = malloc(size);
	if (x == NULL) {
		fprintf(stderr, "There is no enough memory to allocate.\n");
		exit(3);
	}
	return x;
}
 
int main(int argc, char **argv) {
	int optc, tofile;
	int i, j;
	int width, height, chars;
	char **color, rgb[7];
	int one, pos;
	int *pascal;
	char *line;
 
	program_name = argv[0];
	while ((optc = getopt_long(argc, argv, "hm:p:s:", longopts, (int *)0)) != -1) {
		switch (optc) {
		case 'h':
			help();
			return 0;
		case 'm':
			mod = atoi(optarg);
			if (mod > 48) { /* XXX */
				fprintf(stderr, "At the moment, this program supports only mod <= 48 (color generation limit).\n");
				return 4;
			}
			if (mod > 26) { /* XXX */
				fprintf(stderr, "At the moment, this program supports only mod <= 26 (bad implementation limit).\n");
				return 5;
			}
			break;
		case 'p':
			padding = atoi(optarg);
			break;
		case 's':
			size = atoi(optarg);
			break;
		default:
			try_help();
			return 1;
		}
	}
	if (optind < argc && strcmp("-", argv[optind])) {
		if (freopen(argv[optind], "w", stdout) == NULL) {
			fprintf(stderr, "Can't open `%s' for writing.\n", argv[optind]);
			return 2;
		}
		tofile = 1;
	} else {
		tofile = 0;
	}
 
	printf("static char *a_xpm[] = {\n");
	width = size * 2 + padding * 2;
	height = size * 2 + padding * 2;
	chars = baselog(mod, 26);
	printf(""%d %d %d %d",\n", width, height, mod + 1, chars);
	color = xmalloc(sizeof(color[0]) * (mod+1));
	rgb[6] = '�';
 
	printf("\"");
	color[mod] = xmalloc(sizeof(color[mod][0]) * (chars + 1));
	for (j = 0; j < chars; j++) {
		color[mod][j] = ' ';
	}
	color[mod][chars] = '�';
	printf("%s c #000000\",\n", color[mod]);
 
	for (i = 0; i < mod; i++) {
		color[i] = xmalloc(sizeof(color[i][0]) * (chars + 1));
		if (i == 0) {
			for (j = 0; j < chars; j++) {
				color[i][j] = 'a';
			}
			color[i][chars] = '�';
		} else {
			strcpy(color[i], color[i-1]);
			for (j = chars-1; j >= 0; j--) {
				if (color[i][j] == 'z') {
					color[i][j] = 'a';
				} else {
					color[i][j]++;
					break;
				}
			}
		}
		one = 255 / pow(2, i / 6);
		sprintf(rgb, "%02x%02x%02x", makecolors[i%6][0] == '1' ? one : 0,
				makecolors[i%6][1] == '1' ? one : 0, makecolors[i%6][2] == '1' ? one : 0);
		printf("\"%s c #%s\",\n", color[i], rgb);
	}
 
	line = xmalloc(sizeof(line[0]) * (width+1));
	pascal = xmalloc(sizeof(pascal[0]) * size);
 
	line[width] = '�';
	for (j = 0; j < width; j++) {
		line[j] = ' ';
	}
	for (i = 0; i < padding; i++) {
		printf("\"%s\",\n", line);
	}
 
	memset(pascal, 0, sizeof(pascal[0]) * size);
	pascal[0] = 1;
 
	for (i = 0; i < size; i++) {
		for (j = i; j >= 0; j--) {
			if (j != 0) {
				pascal[j] = (pascal[j-1] + pascal[j]) % mod;
			}
			pos = padding + 2*j + (size - 1 - i);
			/* XXX a implementacao de line ficou toda errada e so estou pegando
			 * o primeiro caractere de color aqui. precisa ser reescrito. */
			line[pos] = line[pos+1] = *color[pascal[j]];
		}
		printf("\"%s\",\n\"%s\"%s\n", line, line, i == size-1 && !padding ? "" : ",");
	}
 
	line[width] = '�';
	for (j = 0; j < width; j++) {
		line[j] = ' ';
	}
	for (i = 0; i < padding; i++) {
		printf("\"%s\"%s\n", line, i == padding-1 ? "" : ",");
	}
	printf("};\n");
 
	if (tofile) {
		fclose(stdout);
	}
	return 0;
}

(Download — 5kb)

Escada perfeita (OBI2006)

ATENÇÃO: Este conteúdo foi publicado há 10 anos. Eu talvez nem concorde mais com ele. Se é um post sobre tecnologia, talvez não faça mais sentido. Mantenho neste blog o que escrevo desde os 14 anos por motivos históricos. Leia levando isso em conta.

Depois de meses sem postar, resolvi que a partir de agora darei mais atenção pra este blog. Muita gente me manda e-mail e comentários com dúvidas e gostaria de deixar bem claro que eu não faço trabalhos de faculdade pra ninguém, mas que se você tiver uma dúvida real onde eu possa ajudar eu ajudarei de bom grado.

Pensei muito sobre o que postar aqui, tenho rascunhos sobre buscas em grafos e sobre resoluções de problemas de grafos, mas resolvi quebrar toda a ordem e, a partir de um scrap de orkut, acabei me lembrando do problema Escada perfeita, da OBI 2006, e me deu vontade de resolvê-lo aqui.

Por que o problema Escada Perfeita?

A programação deste problema é extremamente simples, mas a sua lógica (matemática pura) é muito inteligente. Tente resolver o problema antes de ver minha solução e, caso não consiga, depois veja como a solução é bonita.

Vamos ao enunciado…

Uma construtora, durante a criação de um parque temático, encontrou no terreno um conjunto de várias pilhas de cubos de pedra. Ao invés de pagar pela remoção dos cubos de pedras, um dos arquitetos da empresa achou interessante utilizar as pedras para decoração do parque, determinando que as pedras fossem rearranjadas no formato de “escada”. para isso, os funcionários deveriam mover alguns cubos para formar os degraus das escadas. Só que o arquiteto decidiu que, entre uma pilha e outra de pedras deveria haver exatamente uma pedra de diferença, formando o que ele chamou de escada perfeita. O exemplo abaixo mostra um conjunto de cinco pilhas de pedras encontradas e as cinco pilhas como ficaram após a arrumação em escada perfeita.

Tarefa

Dada uma seqüência de pilhas de cubos de pedras com suas respectivas alturas, você deve determinar o número mínimo de pedras que precisam ser movidas para formar uma escada perfeita com exatamente o mesmo número de pilhas de pedras encontrado inicialmente (ou seja, não devem ser criadas ou eliminadas pilhas de pedras). O degrau mais baixo da escada deve sempre estar do lado esquerdo.

Entrada

A entrada contém um único conjunto de testes, que deve ser lido do dispositivo de entrada padrão (normalmente o teclado). A primeira linha contém um inteiro n que indica o número de pilhas de pedras. A segunda linha contém N números inteiros que indicam a quantidade de cubos de pedras em cada uma das pilhas, da esquerda para a direita.

Saída

Seu programa deve imprimir, na saída padrão, uma única linha, contendo um inteiro: o número mínimo de cubos de pedras que devem ser movidos para transformar o conjunto de pilhas em uma escada perfeita, conforme calculado pelo seu programa. Caso não seja possível efetuar a transformação em escada perfeita, imprima como resultado o valor -1.

Exemplos

Exemplo 1

Entrada
5
5 4 5 4 2

Saída
5

Exemplo 2

Entrada
6
9 8 7 6 5 4

Saída
9

Exemplo 3

Entrada
2
1 5

Saída
-1

OK. Estão prontos?

Depois de pensar um pouco, conclui-se que:

  1. A escada perfeita é uma PA de razão 1 (aumenta de um em um). Você lembra disso do seu primeiro ano do Ensino Médio? Senão, é bom dar uma relembrada. As fórmulas (simples e facilmente deduzíveis) da PA são:

    Termo geral da PA

    a_{n} = a_{1} + (n-1).r

    Soma de PA

    \displaystyle S_{n} = (a_{1}+a_{n}).\frac{n}{2}

  2. Sabemos quanto vale n (o número de pilhas, número de elementos da PA) e conseguimos calcular a soma de todos os elementos (podemos fazer isso até durante o loop da entrada, certo?)
  3. Sabemos quanto vale a razão (r=1).
  4. Substituindo o que sabemos nas fórmulas conseguimos formar um sistema de equações básico e desta forma torna-se fácil descobrir o valor do primeiro e do último termo da PA (a_{1} e a_{n}). Resumindo um pouco os cálculos, depois de alguma manipulação algébrica você chega a: \displaystyle a_{n} = \frac{\frac{2.S_{n}}{n}+n-1}{2}

    \displaystyle a_{1} = 1 + a_{n} - n

  5. Agora que já sabemos onde começa e onde termina a escada basta fazer um loop em cada fila de blocos e adicionar à uma variável movimentos a quantidade de quadradinhos que estão sobrando nesta fileira (por exemplo, na primeira fileira da figura do enunciado está sobrando três quadradinhos para chegar ao a_{1}=2). Ao mesmo tempo, adicionamos à outra variável (moves) a quantidade de quadradinhos que devem ser retirados ou colocados na fileira (porque depois se esta variável não for igual a 0 imprimimos -1). Ficou claro?

Implementação

Variáveis

  • n: número de degraus (fileiras de blocos)
  • a: a_{1}, número de blocos do primeiro degrau.
  • b: a_{n}, número de blocos do último degrau.
  • soma: S_{n}, soma da PA.
  • pilha[]: vetor de degraus.
  • movimentos e moves: explicados no quinto passo da solução.
  • i e j: variáveis auxiliares para fazer os loops.

Codeado em C

#include <stdio.h>
#define PMAX 10001
 
int main() {
    int i, j;
    int n;
    int soma=0;
    int a, b;
    int pilha[PMAX];
    int moves=0;
    int movimentos=0;
 
    scanf("%d", &n);
    for (i=0; i<n; i++) {
        scanf("%d", &pilha[i]);
        soma+=pilha[i];
    }
 
    b=(((2*soma)/n)+(n-1))/2;
    a=1+b-n;
 
    for (i=0; i<n; i++) {
        moves+=(pilha[i]-(i+a));
        if (pilha[i]>i+a) {
            movimentos+=(pilha[i]-(i+a));
        }
    }
 
    if (moves!=0) {
        printf("-1n");
    } else {
        printf("%dn", movimentos);
    }
 
    return 0;
}

Prontinho! Qualquer dúvida escrevam seus comentários.

OBI2007 – Primeira fase

ATENÇÃO: Este conteúdo foi publicado há 10 anos. Eu talvez nem concorde mais com ele. Se é um post sobre tecnologia, talvez não faça mais sentido. Mantenho neste blog o que escrevo desde os 14 anos por motivos históricos. Leia levando isso em conta.

Não divulgarei minhas soluções (aka gabarito… :p) até sexta-feira, que é quando os professores já vão ter enviado a prova para a comissão organizadora da Olimpíada Brasileira de Informática.

Ontem, sábado 17 de março, foi a primeira fase da OBI. Eu resolvi a Iniciação Nível 1 para me divertir, vi a prova da Programação Nível 1 (que a Carol resolveu, com uma noção de C muito boa adquirida em um mês) e solucionei a prova da Programação Nível 2. Só vou falar sobre ela por enquanto, depois crio outros posts para falar sobre as outras.

Pra começar, a prova estava fácil. Eu resolvi em duas horas. Isso foi uma opinião de todos que fizeram a prova. Creio que estava mais fácil que a do ano passado. O caderno de tarefas era composto por cinco questões:

  • Chocolate – Uma barra de chocolate é dividida várias vezes. O objetivo do programa é contar a quantidade de pedaços em que ela foi dividida. Basta ir pegando os números e ir somando-os -1.
  • Repositório – Uma lista de números de programas e a versão em que estão instalados num computador. Depois, uma lista de números de programas e a versão em que eles estão disponíveis na internet. Decidir que programas devem ser atualizados no computador (determinar sempre a maior versão) e imprimí-los.
  • Pastas – Dada uma lista de inteiros, verificar se os números aparecem a mesma quantidade de vezes (ex.: 1, 1, 2, 2, 3, 3 é válido; mas 1, 2, 2, 3, 3 não é).
  • Móbile – Interpretei como um problema de grafos. Sabe o que é um móbile? Você tem que ver se todos as peças de um mesmo “nível” tem a mesma quantidade de filhos. Eu fiz um BFS (busca em largura) para determinar o nível de cada um e depois foi só ver se todos de cada nível tinham a mesma quantidade de filhos.
  • Sacoleiro – Um cara quer comprar presentes para seus filhos. Em cada cidade há presente para um ou para outro, com preços diferentes. Ele quer ser justo. Seguindo um trajeto possível, passando por uma cidade e comprando um ou mais presentes ou até nenhum, qual a menor diferença possível entre os preços dos presentes? Sem dúvidas o problema mais difícil da prova (creio que o único difícil). Ainda não conheço a solução ideal, que deve usar programação dinâmica. Implementei um DFS (busca em profundidade) que testa todas as possibilidades.

No orkut já me disseram que cometi um erro ridículo:

No terceiro parágrafo do Repositórios, ele diz: “Um programa deve verificar então qual a versão de cada programa instalado nos computadores (todos eles possuem os mesmos aplicativos instalados e nas mesmas versões) e INSTALAR TODOS AQUELES QUE AINDA NÃO FORAM INSTALADOS ou cuja versão instalada seja anterior a versão mais recente.” Portanto, se um programa disponível na internet não está instalado nos computadores, ele deve ser instalado.

E meu Sacoleiro provavelmente não vai passar no tempo. Então espero 300 pontos e alguns quebrados.

O resultado sairá até o dia 7 de abril lá no site deles. Eu gero uma lista de classificação quando sair. :)

for (d=hoje; d<=17/03; d++) { Estude – OBI }

ATENÇÃO: Este conteúdo foi publicado há 11 anos. Eu talvez nem concorde mais com ele. Se é um post sobre tecnologia, talvez não faça mais sentido. Mantenho neste blog o que escrevo desde os 14 anos por motivos históricos. Leia levando isso em conta.

IMPORTANTE: Esse post não é recomendado pra quem nunca programou. Escrevi sem pensar neles… :-)

Bom… Existem pessoas que sabem programar e não programam. O difícil na arte de programar é pensar, porque o resto é escrever em inglês e se acostumar com uma sintaxe rigorosa.

Comecei ontem a ensinar um amigo a programar em C para participar da OBI 2007, que foi anunciada nessa semana. Eu poderia ensinar Pascal, que é mais que suficiente para olimpíadas (quem conhece o André Linhares entende o que eu quero dizer…), mas resolvi ensinar C porque eu me embabacaria no Pascal e no C eu vejo os blocos mais “definidos” com as chaves; aqueles begins e ends “sujam” o código. E como diz o lema do sistema desse blog, code is poetry.

O reverendo e meus leitores mais novos devem estar se perguntando: como o Tiago é capaz de fazer essas loucuras? É verdade que fiquei um bom tempo sem escrever sobre programação, mas adoro isso! É lazer pra mim e essa também é a minha profissão, já que eu não consigo viver desse blog (por culpa sua que não clica nos meus anúncios…). Só quando começo a brincar é que lembro como é divertido e acho que é porque eu me sinto “no poder”. :-)

Mas voltando ao assunto… Esse meu colega é campeão regional de matemática e tem uma facilidade incrível para matérias exatas (e pras humanas mais ainda, eu acho). Eu estava sem nada pra escrever aqui no blog e resolvi escrever sobre o que eu vou ensinar pra ele amanhã: arrays e for.

Meu aluno está resolvendo a prova da Programação Nível 1 da OBI2005. Ele já resolveu a Frota de Táxi e agora precisa resolver o problema Campos de Minhoca.

O problema é que, pela primeira vez, ele se depara com uma situação em que tem que receber como entrada uma tabela completa! Sugeri que ele usasse dois while, um dentro do outro. Ele pensou um pouco e conseguiu fazer o seguinte código:

scanf("%d %d", &n, &m);
 
natual=1;
while (natual<=n) {
	matual=1;
	while (matual<=m) {
		scanf("%d", &valor);
		matual=matual+1;
	}
	natual=natual+1;
}

Perfeito. Era o que eu queria que ele fizesse. Mas agora entenda sua situação: como armazenar todos esses números pra depois trabalhar com eles?

Dessa maneira, cada vez que recebemos um novo elemento da tabela, colocamos numa variável valor e ao final do recebimento da entrada ficaremos apenas com o último elemento da tabela.

E então entram os arrays…

Arrays são matrizes de matemática ou, numa língua muito mais fácil, tabelas. Vamos supôr que eu receba 1000 valores e queira saber qual é o maior deles. Imaginem como seria para declarar suas variáveis, recebê-los e tratá-los:

int var1, var2, var3, var4, ..., var1000;
 
scanf("%d", &var1);
scanf("%d", &var2);
scanf("%d", &var3);
scanf("%d", &var4);
...
scanf("%d", &var1000);
 
if (var1>maior) {
	maior=var1;
}
 
if (var2>maior) {
	maior=var2;
}
 
if (var3>maior) {
	maior=var3;
}
 
...

Impossível! Totalmente inviável. Então alguém teve a brilhante idéia de criar um elemento que guarda várias variáveis de uma vez. Então surgiram os arrays. Você cria uma só variável e na sua declaração coloca o número de elementos que ele tem dentro de chaves.

int var[1001];

Depois para receber os valores você pode então simplesmente usar o while como usou no exemplo do Campos de Minhoca:

int var[1001], indice;
 
indice=1;
while (indice<=1000) {
	scanf("%d", &var[indice]);
	indice=indice+1;
}

E para ver qual é o maior deles basta usar mais um while:

indice=1;
while (indice<=1000) {
	if (var[indice]>maior) {
		maior=var[indice];
	}
}

Mas peraí… Então como faríamos no Campos de Minhoca? Lá não temos só uma lista de N números, mas uma tabela mesmo, com altura e largura. É simples, basta fazer com que cada índice dessa lista seja outra lista.

int tabela[1001][1001];

Assim, podemos acessar todos os elementos e pra saber o elemento da coordenada 5, 23 basta usar a variável tabela[5][23].

Aí aquele primeiro código do Campos de Minhoca torna-se:

scanf("%d %d", &n, &m);
 
natual=1;
while (natual<=n) {
	matual=1;
	while (matual<=m) {
		scanf("%d", &valor[natual][matual]);
		matual=matual+1;
	}
	natual=natual+1;
}

As variáveis [n,m]atual vão crescendo e preenchendo a tabela. :)

Só que acontece que se programássemos dessa maneira gastaríamos uma porção de códigos e ficaríamos confusos pra trabalhar com arrays, tendo que sempre verificar os índices e acabaríamos errando bastante. Então criou-se o for. O for é uma simplificação desse tipo de while. Você diz que:

para todo natual de 1 a n, faça:
	alguma coisa
fim-para

Escrever for em Pascal é super divertido, porque você se sente falando com o computador:

for i:=1 to 100, do begin
	código aqui
end;

No C existe uma sintaxe mais versátil, mas que pode ser um pouquinho mais difícil de entender no início:

for (atribuição; condição; incremento)

A atribuição é onde você coloca o primeiro valor do índice. A condição é a condição para que o enquanto continue funcionando. O incremento é o que ele deve fazer ao final de cada loop (geralmente é aumentar um).

Então, ao invés de fazer esse while:

indice=1;
while (indice<=1000) {
	scanf("%d", &var[indice]);
	indice=indice+1;
}

Você pode escrever:

for (indice=1; indice<=1000; indice=indice+1) {
	scanf("%d", &var[indice]);
}

E como resolver a parte da entrada do Campos de Minhoca sabendo disso?

Simples… Basta colocar um for dentro do outro:

scanf("%d %d", &n, &m);
 
for (i=1; i<=n; i++) {
	for (j=1; j<=m; j++) {
		scanf("%d", &matriz[i][j]);
	}
}

Observação 1: Escrever variavel++ é a mesma coisa que escrever variavel=variavel+1.

Observação 2: Geralmente utiliza-se i para o primeiro for, depois j, k, l e eu nunca tive que passar do l. :)

Observação 3: Se eu queria um vetor de 1000 posições lá em cima, por que eu declarei 1001? Bom… O C conta a partir de 0. Quando eu peço 1000, ele vai me dar um vetor de 0 a 999. Já que eu queria ter o var[1000] eu precisei declarar de 1000+1=1001.

Ficou claro ou muito confuso? Se deu pra entender isso aí, agora é só mandar a ver no resto do problema! :)